Vous êtes sur la page 1sur 34

Programmation back-end &

front-end
ReactJS/NodeJS : connexion à la base de données

Alexandre TELLE // Master MIAGE SITN // Octobre 2023


Plan du cours
ReactJS/NodeJS : connexion à la base de données

1. Enrichir son API NodeJS : connecter une base de données


1. Connexion à une base de données : introduction à SQLite
2. Connexion à une base de données : introduction aux ORM
3. Models
4. Routes

2. Connecter son API à NextJS


3. Exercice pratique

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 2


1. Enrichir son API NodeJS : connecter une base de données
SQLite : introduction et initialisation

SQLite est un moteur de base de données relationnelle, écrite en C, accessible via SQL.

Contrairement aux serveurs de bases de données traditionnels, comme MySQL ou PostgreSQL, sa particularité est de ne pas reproduire
le schéma habituel client-serveur mais d'être directement intégrée aux programmes. L'intégralité de la base de données (déclarations,
tables, index et données) est stockée dans un chier indépendant de la plateforme.

Grâce à son environnement intégré au programme et le fait que le code source ne soit régit par aucune licence, SQLite est utilisé dans de
nombreux logiciels et systèmes bien connus tels que Firefox, Skype, Android, l’iPhone et divers produits et projets.

Ça en fait donc un candidat idéal pour exécuter des tests, sans avoir à mobiliser un serveur entier pour sa base de données. C’est la base
de données que nous allons utiliser dans le reste de ce cours.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 3


fi
1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Un ORM (Object-Relational Mapping) est une interface qui se place entre un programme (typiquement votre backend) et une base de
données relationnelle.

Cela permet de simplifier l’accès à une base de données en proposant au développeur d’utiliser des objets au lieu d’accéder
directement à la base de données relationnelle.

Dans le cas d’une base de données non relationnelle, on parle d’ODM (Object-Document Mapping).

Les avantages à utiliser un ORM sont :


• Réduction du code à créer et à maintenir
• Homogénéité du code objet
• Pas besoin d’écrire du SQL
• Tampon pour les injections SQL

Les inconvénients :
• Les requêtes très complexes sont plus évidentes à écrire en SQL
• L’ORM masquant la complexité et abstrayant pas mal de concepts, il est nécessaire d’avoir les connaissances théoriques et de connaître
le SQL pour éviter d’avoir une conception bancale de notre base de données

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 4


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Pour chaque langage et chaque base de données, il y a des ORM/ODM :

• PHP : Doctrine que l’on retrouve dans le framework Symfony notamment ou Eloquent utilisé dans le framework Laravel ;
• Python : SQLAchemy, Django, Orator
• NodeJS / Javascript : Mongoose, Sequelize, TypeORM, Prisma
• Ruby : Active record
• Java : Hibernate, JDO, Java Persistence API, TopLink
• .NET : Entity Framework, Linq To SQL ou encore NHibernate ou Dapper

Et bien d’autres encore.

Pour la suite du cours, nous allons utiliser Sequelize.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 5


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Pour la suite de ce cours, nous allons créer une application qui permet de prendre des notes. Nous ferons l’API, la base de données et
le frontend.

D’abord commençons par l’API. Créons une nouvelle api, pour cela créons un nouveau dossier notes-api dans lequel on exécutera la
commande npm init.

Nous installerons ensuite les packages dont nous aurons besoin :


npm install express nodemon sequelize sqlite3 --save

Une fois fait, nous pouvons créer un fichier index.js, et nous allons également créer un dossier src, qui nous servira plus tard.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 6


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

CommonJS vs ES6

Lors du cours 2 nous avons parlé des nouveautés d’ES6 (ECMAScript 6), qui sont largement utilisées dans les frameworks frontend.
L’essentiel de la différence se trouve dans la gestion des modules.

Dans le développement de logiciels modernes, les modules organisent le code logiciel en morceaux autonomes qui, ensemble,
constituent une application plus vaste et plus complexe.

Dans l'écosystème JavaScript du navigateur, l'utilisation de modules JavaScript dépend des instructions import et export ; ces
instructions chargent et exportent respectivement des modules ECMAScript (ou modules ES).

Le format de module ES est le format standard officiel pour empaqueter le code JavaScript en vue de sa réutilisation et la plupart des
navigateurs web modernes prennent en charge les modules de manière native.

Node.js, cependant, prend en charge le format de module CommonJS par défaut. Les modules CommonJS se chargent à l'aide de
require(), et les variables et fonctions s'exportent à partir d'un module CommonJS avec module.exports.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 7


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Pour la suite du cours, nous allons introduire la gestion des modules ES6 dans NodeJS.

Pour cela, il faut modifier son fichier package.json et y ajouter la ligne suivante (de préférence parmi les premières lignes) :
"type": "module",

De ce fait, nous aurons un système d’import beaucoup plus cohérent avec notre frontend. Ainsi, au lieu d’appeler Express de cette
manière :
const express = require('express');

Nous l’appellerons en utilisant cette commande :


import express from 'express';

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 8


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Maintenant que tous nos packages sont installés, avant de remplir notre fichier index.js, nous allons créer un autre fichier à la racine
qui s’appelle db.js.

Ce fichier va contenir notre initialisation de Sequelize et SQLite. Il contiendra les lignes suivantes :

import { Sequelize } from "sequelize";

// Initialize Sequelize
const sequelize = new Sequelize({
// The `host` parameter is required for other databases
// host: 'localhost'
dialect: 'sqlite',
storage: './database.sqlite'
});

// Launch the database


sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});

export default sequelize;

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 9


1. Enrichir son API NodeJS : connecter une base de données
ORM : introduction et installation

Ainsi, dans notre fichier index.js nous aurons notre code de base, que nous connaissons, avec quelques différences : la nouvelle
syntaxe d’import, et nous n’y définirons pas les routes (nous les mettrons ailleurs). Nous appellerons également le fichier db.js afin
d’initialiser la base de données.

import express from 'express';


import sequelize from './db.js';

// Initialize Express
const app = express();
const port = 3001;

app.use(express.json());

// Launch the server


app.listen(port, () => {
console.log(`Server Started at ${port}`)
});

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 10


1. Enrichir son API NodeJS : connecter une base de données
Models

Nous allons définir ensuite des modèles. Pourquoi ? Vu que nous utilisons un ORM, et que ce dernier utilise des objets, on va devoir créer un modèle
(basiquement quelque chose qui se rapproche d’une classe) afin de pouvoir l’écrire ensuite dans la base de données, ou même le récupérer.

Dans le dossier src, nous allons créer un sous-dossier qui s’appelle models. Nous allons créer un fichier note.model.js qui contiendra le modèle
de notre objet Note.

import sequelize from "../../db.js";


import { DataTypes } from "sequelize";

const Note = sequelize.define('Note', {


title: {
type: DataTypes.STRING
},
note: {
type: DataTypes.TEXT,
allowNull: false
},
tag: {
type: DataTypes.STRING
}
});

export default Note;

Pour avoir plus de détails sur les types disponibles et comment mieux définir des modèles dans Sequelize, rendez-vous ici.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 11


1. Enrichir son API NodeJS : connecter une base de données
Models

Maintenant que le modèle a été créé, il faut maintenant le lier à notre instance de base de données (notre objet sequelize,
remember?).

Pour ce faire, nous allons utiliser la méthode sync() de Sequelize. Nous pouvons synchroniser les modèles un par un (nous en avons
qu’un seul tant mieux !), tout comme nous pouvons tous les synchroniser en même temps (plus facile) : c’est la seconde méthode que
nous choisirons.

Dans notre fichier index.js nous allons rajouter les lignes suivantes (juste avant l’export default) :

// Synchronize all models


sequelize.sync({ force: true }).then(() => {
console.log('Database & tables created.');
});

Les lignes précédentes vont créer les tables dans notre base de données si elles n’existent pas, et l’option force : true indique qu’il
faut les supprimer au préalable si jamais elles existent. Dans une application de production, il est préférables d’utiliser des migrations
plutôt que la méthode sync(), mais dans le cadre de ce cours, ce sera bien plus que suffisant.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 12


1. Enrichir son API NodeJS : connecter une base de données
Models

Maintenant que tout a été défini… Comment pouvons-nous le vérifier ? Vu que SQLite est une base de données stockées dans un fichier,
on ne dispose pas des mêmes outils que pour une base de données traditionnelle.

Nous allons télécharger un viewer pour pouvoir regarder à l’intérieur de notre base SQLite comme on le ferait avec MySQL ou
PostgreSQL.

Pour Windows :
• DB Browser for SQLite (https://sqlitebrowser.org)

Pour Mac :
• TablePlus (https://tableplus.com)
• DB Browser for SQLite (https://sqlitebrowser.org)

Solution online :
• SQLite Viewer Web App (https://sqliteviewer.app)

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 13


1. Enrichir son API NodeJS : connecter une base de données
Routes

Ok mais… Je ne vois rien, c’est normal ? Oui, car au final, même si on a appelé la méthode sync(), à aucun moment on a importé notre
fichier note.model.js. On pourrait le faire, mais il y aura une manière plus naturelle de le faire : vu que pour notre API nous allons
définir des routes, nous l’appellerons à ce moment.

Nous allons créer un dossier routes dans notre dossier src. Dans ce dossier, nous allons créer ensuite un nouveau fichier nommé
note.route.js.

import express from 'express';


import Note from '../models/note.model.js';

// Initialize the router


const routerNote = express.Router();

// Routes
routerNote.get('/', getAllNotes);

export default routerNote;

// Functions
function getAllNotes(req, res) {
res.json({ "message" : "Route en cours de création" })
}

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 14


1. Enrichir son API NodeJS : connecter une base de données
Routes

Une fois fait, nous pouvons modifier notre fichier index.js et y rajouter notre route.

import express from 'express';


import sequelize from './db.js';

import routerNote from './src/routes/note.route.js';

// Initialize Express
const app = express();
const port = 3001;

app.use(express.json());

// Apply routes
app.use(‘/notes', routerNote);

// Launch the server


app.listen(port, () => {
console.log(`Server Started at ${port}`)
});

// Synchronize all models


sequelize.sync({ force: true }).then(() => {
console.log('Database & tables created.');
});

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 15


1. Enrichir son API NodeJS : connecter une base de données
Routes

SI maintenant nous vérifions l’état de notre base de données, tout est bien créé.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 16


1. Enrichir son API NodeJS : connecter une base de données
Routes

Maintenant définissons plus en détail nos routes, et interagissons avec la base de données.

Commençons par une route qui permet de créer une note. D’abord la fonction à associer :

function createNote(req, res) {


Note.create({
title: req.body.title,
note: req.body.note,
tag: req.body.tag
}).then(function(note) {
res.json(note);
});
}

Par la suite nous pouvons rajouter au niveau de la définition de nos routes :


routerNote.post('/', createNote);

Maintenant essayons notre route sur Postman.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 17


1. Enrichir son API NodeJS : connecter une base de données
Routes

Si nous appelons en POST http://localhost:3001/notes avec le JSON suivant :


{
"title" : "Courses du weekend",
"note" : "Acheter du pain, des oeufs et du lait",
"tag" : "Courses"
}

Nous obtenons :
{
"id": 1,
"title": "Courses du weekend",
"note": "Acheter du pain, des oeufs et du lait",
"tag": "Courses",
"updatedAt": "2023-10-19T16:50:26.418Z",
"createdAt": "2023-10-19T16:50:26.418Z"
}

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 18


1. Enrichir son API NodeJS : connecter une base de données
Routes

Parfait ! Attaquons-nous maintenant à la route GET que nous avions défini auparavant. Nous modifions la fonction getAllNotes.

function getAllNotes(req, res) {


Note.findAll().then(notes => res.json(notes));
}

En appelant en GET http://localhost:3001/notes nous obtenons :

[
{
"id": 1,
"title": "Courses du weekend",
"note": "Acheter du pain, des oeufs et du lait",
"tag": "Courses",
"createdAt": "2023-10-19T16:58:17.359Z",
"updatedAt": "2023-10-19T16:58:17.359Z"
}
]

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 19


1. Enrichir son API NodeJS : connecter une base de données
Routes

Ensuite, nous pouvons créer une route qui permet de récupérer une note uniquement via son id. Ci-dessous la fonction :

function getNoteById(req, res) {


Note.findByPk(req.params.id).then(note => res.json(note));
}

Sequelize offre une fonction findByPK qui permet de récupérer un résultat via une clé unique. Très pratique. Maintenant la route :
routerNote.get('/:id', getNoteById);

Nous obtenons ce résultat :

{
"id": 1,
"title": "Courses du weekend",
"note": "Acheter du pain, des oeufs et du lait",
"tag": "Courses",
"createdAt": "2023-10-19T17:24:45.942Z",
"updatedAt": "2023-10-19T17:24:45.942Z"
}
Alexandre TELLE // Master MIAGE SITN // Octobre 2023 20
1. Enrichir son API NodeJS : connecter une base de données
Routes

Ce n’est pas encore fini ! Nous allons créer maintenant une route qui permet de mettre à jour une note :

function updateNote(req, res) {


Note.findByPk(req.params.id).then((note) => {
note.update({
title: req.body.title,
note: req.body.note,
tag: req.body.tag
}).then((note) => {
res.json(note);
});
});
}

Et la route associée :

routerNote.put('/:id', updateNote);

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 21


1. Enrichir son API NodeJS : connecter une base de données
Routes

Et enfin, une route qui permet de supprimer une note :

function deleteNote(req, res) {


Note.findByPk(req.params.id).then((note) => {
note.destroy();
}).then((note) => {
res.sendStatus(200);
});
}

Et la route associée :

routerNote.delete('/:id', deleteNote);

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 22


1. Enrichir son API NodeJS : connecter une base de données
Routes
import express from 'express';
import Note from '../models/note.model.js';

// Initialize the router


const routerNote = express.Router();

// Routes
routerNote.get('/', getAllNotes);
routerNote.post('/', createNote);
routerNote.get('/:id', getNoteById);
routerNote.put('/:id', updateNote);
routerNote.delete('/:id', deleteNote);

export default routerNote;

// Functions
function getAllNotes(req, res) {
Note.findAll().then(notes => res.json(notes));
}

function getNoteById(req, res) {


Note.findByPk(req.params.id).then(note => res.json(note));
}

function createNote(req, res) {


Note.create({
title: req.body.title,
note: req.body.note,
tag: req.body.tag
}).then(note => res.json(note));
}

function updateNote(req, res) {


Note.findByPk(req.params.id).then((note) => {
note.update({
title: req.body.title,
note: req.body.note,
tag: req.body.tag
}).then((note) => {
res.json(note);
});
});
}

function deleteNote(req, res) {


Note.findByPk(req.params.id).then((note) => {
note.destroy();
}).then((note) => {
res.sendStatus(200);
});
}

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 23


2. Connecter son API à NextJS
Introduction

Notre API est prête ! Nous n’avons certes pas mis d’authentification, de contrôles accrus, mais nous avons les fonctionnalités de base. Le
code complet se trouve ici.

Construisons maintenant un frontend avec NextJS pour gérer nos notes. Vous trouverez une coquille du frontend ici.

Il y a plusieurs façons de récupérer des données depuis une API en React/NextJS :


• On peut utiliser une librairie externe telle que Axios ou React Query
• Et depuis un peu plus récemment nous pouvons utiliser l’API Fetch (en utilisant la méthode fetch())

L’API Fetch est une API native en JavaScript. Axios est une librairie qui peut être facilement installé dans un projet React/Vue/Angular à
l'aide de NPM.

Contrairement à Axios, fetch() est intégrée dans la plupart des navigateurs modernes. Avec fetch, pas besoin d’installer un package
externe. Au niveau des performances et de l’intégration, il y a peu de différences entre ces solutions.

Axios et d’autres librairies externes peuvent être plus faciles à utiliser dans certains cas, notamment lorsque l’on a un besoin plus
complexe.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 24


2. Connecter son API à NextJS
Introduction

Je vois un dossier /api dans le dossier /pages… Pourquoi on ne l’utiliserait pas ?

C’est tout à fait possible également. Next.js offre la possibilité de créer des API Routes, qui permettent de créer facilement un endpoint
d'API en tant que fonction Node.js serveless.

Tout fichier à l'intérieur du dossier pages/api est mappé à /api/* et sera traité comme un endpoint de l'API au lieu d'une page. Ils ne
sont exécutés uniquement côté serveur et n'augmenteront pas la taille du bundle côté client (et donc on peut y mettre du code serveur
de façon safe).

Dans le cadre de ce cours, nous allons privilégier des façons plus traditionnelles : elles vous permettront par la suite de transférer cette
connaissance vers d’autres frameworks, et de comprendre le fonctionnement traditionnel d’une communication frontend/backend.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 25


2. Connecter son API à NextJS
Data fetching avec NextJS

NextJS nous propose deux fonctions qui nous permettent de pre-render des pages en faisant appel à une :
• getStaticProps
• getServerSideProps

getStaticProps

Cette fonction est idéale pour les sites statiques où il n’y aura pas de refresh dépendant des actions de l’utilisateur. Par exemple, un blog
pour lequel on récupère les articles depuis une API. Avec cette fonction NextJS veut accélérer la performance de votre site en chargeant
ce contenu uniquement au moment du build (build time).

Plus en détails : le build time est le moment où le site est déployé. NextJS parcourt le code et crée des pages statiques. On utilise
getStaticProps, lorsque l’on s’attend à ce que la page soit statique; les données utilisées par la page ne vont pas changer entre le
moment de la création de la page et le moment où celle-ci est requêtée par celui qui la demande. Un post de blog est un bon exemple
de ce type de données. Le contenu d'un post de blog n'est pas susceptible de changer après la création de la page, de telle sorte que
ces données peuvent être fournies sous la forme d'un élément statique. La page n'est rendue qu'une seule fois et est servie à chaque
utilisateur qui la demande.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 26


2. Connecter son API à NextJS
Data fetching avec NextJS

getServerSideProps

Comme vous l’aurez compris, getServerSideProps est utilisé dans le cas où les actions de l’utilisateurs vont modifier les données qui
sont affichées à l’écran. Cette fonction est exécutée uniquement côté serveur, et ne tourne jamais côté navigateur. Ainsi, lorsqu’une page
est demandée, getServerSideProps va s’exécuter et va retourner le résultat pré-rendu avec les propriétés et données nécessaires.

getServerSideProps ne s’appelle que depuis une page, et ne marche pas si il est appelé depuis un fichier qui n’est pas une page. Elle
est appelé au moment de la requête (request time).

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 27


2. Connecter son API à NextJS
Data fetching avec NextJS

Du coup, getStaticProps vs getServerSideProps ?

Ces deux fonctions sont très utiles dans Next lorsque l’on veut pre-render des pages sur notre site. Pourquoi pre-render des pages ?
Principalement à cause du SEO. Lorsque l’on a une application dynamique, il est plus difficilement de gérer le côté référencement. Next
permet de faciliter tout ça.

Néanmoins tous les sites ne nécessitent pas d’avoir un SEO optimisé : vous avez un site où un utilisateur se connecter, qui présente des
dashboard ? Généralement pas besoin de SEO. Ce sont des page privée, spécifique à un utilisateur.

Dans le cadre de notre cours, nous n’utiliserons ni getStaticProps, ni getServerSideProps. Mais il est important de savoir que ces
fonctions existent dans Next pour vous permettre de rendre votre site plus performant.

Maintenant, voyons comment récupérer les données de notre API et comment les afficher.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 28


2. Connecter son API à NextJS
Data fetching avec NextJS

Nous allons voir comment faire du client-side fetching avec NextJS. Précédemment, nous avons vu des fonctions nous permettant de
faire du server-side fetching.

L’avantage du client-side fetching, est qu’on peut le faire depuis un composant. C’est aussi une méthode que vous pourrez utiliser
quelque soit le framework, et non dépendante de NextJS.

React met à disposition un hook qui permet de synchroniser un composant avec un système externe (typiquement un API) : useEffect.
Note : nous avons abordé les hooks lors des cours précédents, n’hésitez pas à vous y référer.

Nous allons utiliser useEffect conjointement avec useState pour appeler notre API.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 29


2. Connecter son API à NextJS
Data fetching avec NextJS

Ainsi, nous pouvons mettre à jour notre composant List :

import { useState, useEffect } from 'react'


import Note from "./Note"

export default function List() {


const [notes, setNotes] = useState([])

useEffect(() => {
fetch('http://localhost:3001/notes')
.then((res) => res.json())
.then((data) => {
setNotes(data)
})
}, [])

return (
<ul role="list" className="divide-y divide-gray-100">
{notes.map((note) =>
<Note key={note.id} note={note} />
))}
</ul>
)
}

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 30


2. Connecter son API à NextJS
Data fetching avec NextJS

Il se peut qu’à la sauvegarde vous ayez cette erreur :

La raison ? CORS. En anglais Cross-Origin Resource Sharing, il s’agit d’un mécanisme qui consiste à ajouter des en-têtes HTTP afin de
permettre à un agent utilisateur d'accéder à des ressources d'un serveur situé sur une autre origine que le site courant.

Plus concrètement, quiconque appelle un site Web ne doit pas charger d’autres données venant de serveurs externes. Votre frontend
étant exécuté sur un serveur différent que votre API, il faut pouvoir les faire communiquer.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 31


2. Connecter son API à NextJS
Data fetching avec NextJS

Pour cela, retournons du côté de notre API Express. Nous allons installer le package CORS :
npm install cors

Ensuite, dans notre code nous allons rajouter juste après la ligne app.use(express.json()) :
app.use(cors());

Et voilà ! Rien de plus simple. Dans le cadre de ce cours ce sera suffisant, mais dans une application de production il faudrait limiter ce
comportement : concrètement ici, on permet à tous les serveurs quelque soit leur origine d’accéder à notre API. Mais il faudrait par
exemple établir une liste d’origines approuvées, et ne permette qu’à celles-ci d’accéder à notre API.

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 32


2. Connecter son API à NextJS
Data fetching avec NextJS

De retour à notre application NextJS, nous avons effectivement notre liste mise à jour :

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 33


3. Exercice pratique
Améliorer l’application Notes

Nous avons créé notre API et l’avons connecté à notre frontend.

Maintenant, à vous de jouer :


• Rajouter la fonction pour créer une note
• Créer une fonctionnalité qui permet de supprimer une note
• Créer une fonctionnalité qui permet de modifier une note (indice : réutiliser le formulaire déjà existant)

Alexandre TELLE // Master MIAGE SITN // Octobre 2023 34

Vous aimerez peut-être aussi