Générateur de modèle pour Express
Il y a un Générateur d’applications Express qui permet de générer la structure de base recommandée en Typescript.
Voici comment générer l’application
Situez vous dans le dossier où vous désirez créer l’application (le dossier de l’application sera automatiquement créé à la prochaine étape)
Exécutez la commande de création d’application :
Une fois l’application générée, déplacez-vous dans le dossier créé et installez les modules
Si vous avez des erreurs de modules dépréciés, vous pouvez les corriger en suivant les instructions de la section Module déprécié.
Exécutez l’application en utilisant la commande suivante :
Coder un API Express
L'exemple suivant code une mini API qui gère des réservations dans un hotel.
CodeSandbox
Étape 1 - Créer l’interface model/Reservation.ts
model/Reservation.ts
// **** Variables **** //
const INVALID_CONSTRUCTOR_PARAM =
'nameOrObj arg must a string or an ' +
'object with the appropriate user keys.';
export enum TypeChambre {
Standard,
Deluxe,
}
// **** Types **** //
export interface IReservation {
id: number;
nomClient: string;
courrielClient: string;
dateDebut: string;
dateFin: string;
typeChambre: TypeChambre;
prixParNuit: number;
}
// **** Functions **** //
/**
* Créer une réservation.
*
* @param {string=} nomClient - Le nom du client
* @param {string=} courrielClient - L'adresse de courriel du client
* @param {string=} dateDebut - Date de début de la réservation
* @param {string=} dateFin - Date de fin de la réservation
* @param {TypeChambre=} typeChambre - Le type de chambre réservée
* @param {number=} prixParNuit - Le prix de la chambre par nuit
* @param {number=} id - ID de la réservation dans la BD
*/
function new_(
nomClient?: string,
courrielClient?: string,
dateDebut?: string,
dateFin?: string,
typeChambre?: TypeChambre,
prixParNuit?: number,
id?: number // id last cause usually set by db
): IReservation {
return {
id: id ?? -1,
nomClient: nomClient ?? '',
courrielClient: courrielClient ?? '',
dateDebut: dateDebut ?? '',
dateFin: dateFin ?? '',
typeChambre: typeChambre ?? TypeChambre.Standard,
prixParNuit: prixParNuit ?? 0,
};
}
/**
* Extraire une réservation d'un objet.
*
* @param {object} param - Objet représentant une réservation
*
* @returns {IReservation} - Une réservation
*/
function from(param: object): IReservation {
// Check is réservation
if (!isReservation(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
// Get user instance
const p = param as IReservation;
return new_(
p.nomClient,
p.courrielClient,
p.dateDebut,
p.dateFin,
p.typeChambre,
p.prixParNuit,
p.id
);
}
/**
* Vérifier si l'objet représente une réservation
*
* @param {unknown} arg - Un paramètre qui pourrait être une réservation
*
* @returns {boolean} - Vrai si c'est une réservation
*/
function isReservation(arg: unknown): boolean {
return (
!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'nomClient' in arg &&
'courrielClient' in arg &&
'dateDebut' in arg &&
'dateFin' in arg &&
'typeChambre' in arg &&
'prixParNuit' in arg
);
}
// **** Export default **** //
export default {
new: new_,
from,
isReservation,
} as const;
Étape 2 - Ajouter le modèle à la base de données bidon
repos/MockOrm.ts
import jsonfile from 'jsonfile';
import { IReservation } from '@src/models/Reservation';
// **** Variables **** //
const DB_FILE_NAME = 'database.json';
// **** Types **** //
interface IDb {
reservations: IReservation[];
}
// **** Functions **** //
/**
* Fetch the json from the file.
*/
function openDb(): Promise<IDb> {
return jsonfile.readFile(__dirname + '/' + DB_FILE_NAME) as Promise<IDb>;
}
/**
* Update the file.
*/
function saveDb(db: IDb): Promise<void> {
return jsonfile.writeFile(__dirname + '/' + DB_FILE_NAME, db);
}
// **** Export default **** //
export default {
openDb,
saveDb,
} as const;
Étape 3 - Mettre à jour la base de données
repos/database.json
{"reservations":[{"id":1,"nomClient":"Justin Trudeau","courrielClient":"justin@profinfo.ca","dateDebut":"2023-09-01","dateFin":"2023-09-03","typeChambre":"Deluxe","prixParNuit":150},{"id":716805071644,"nomClient":"Kamala Harris","courrielClient":"kamala@profinfo.ca","dateDebut":"2025-01-06","dateFin":"2025-01-08","typeChambre":"Deluxe","prixParNuit":150},{"id":177404267690,"nomClient":"Kamala Harris","courrielClient":"kamala@profinfo.ca","dateDebut":"2025-01-06","dateFin":"2025-01-08","typeChambre":"Deluxe","prixParNuit":150},{"id":161338412973,"nomClient":"Kamala Harris","courrielClient":"kamala@profinfo.ca","dateDebut":"2025-01-06","dateFin":"2025-01-08","typeChambre":"Deluxe","prixParNuit":150}]}
Étape 4 - Créer le repo
repos/ReservationRepo.ts
import { IReservation } from '@src/models/Reservation';
import { getRandomInt } from '@src/util/misc';
import orm from './MockOrm';
// **** Functions **** //
/**
* Extraire une réservation.
*
* @param {string} courrielClient - Courriel du client
*
* @returns {IReservation | null} - Réservation si trouvée, sinon null.
*/
async function getOne(courrielClient: string): Promise<IReservation | null> {
const db = await orm.openDb();
for (const reservation of db.reservations) {
if (reservation.courrielClient === courrielClient) {
return reservation;
}
}
return null;
}
/**
* Vérifier si une réservation avec l'ID existe
*
* @param {number} id - ID de la réservation
*
* @returns {boolean} - Vrai si la réservation existe
*/
async function persists(id: number): Promise<boolean> {
const db = await orm.openDb();
for (const reservation of db.reservations) {
if (reservation.id === id) {
return true;
}
}
return false;
}
/**
* Extraire toutes les réservations.
*
* @returns {IReservation[]} - Tableau de toutes les réservations
*/
async function getAll(): Promise<IReservation[]> {
const db = await orm.openDb();
return db.reservations;
}
/**
* Ajouter une réservation.
*
* @param {IReservation} reservation - Réservation à ajouter
*/
async function add(reservation: IReservation): Promise<IReservation> {
const db = await orm.openDb();
reservation.id = getRandomInt();
db.reservations.push(reservation);
orm.saveDb(db);
return reservation;
}
/**
* Mettre à jour une réservation
*
* @param {IReservation} reservation - Réservation à mettre à jour
*/
async function update(reservation: IReservation): Promise<void> {
const db = await orm.openDb();
for (let i = 0; i < db.reservations.length; i++) {
if (db.reservations[i].id === reservation.id) {
db.reservations[i] = reservation;
return orm.saveDb(db);
}
}
}
/**
* Supprimer une réservation.
*
* @param {number} id - ID de la réservation à supprimer
*/
async function delete_(id: number): Promise<void> {
const db = await orm.openDb();
for (let i = 0; i < db.reservations.length; i++) {
if (db.reservations[i].id === id) {
db.reservations.splice(i, 1);
return orm.saveDb(db);
}
}
}
// **** Export default **** //
export default {
getOne,
persists,
getAll,
add,
update,
delete: delete_,
} as const;
Étape 5 - Créer le service
services/ReservationService.ts
import ReservationRepo from '@src/repos/ReservationRepo';
import { IReservation } from '@src/models/Reservation';
import RouteError from '@src/common/RouteError';
import HttpStatusCodes from '@src/common/HttpStatusCodes';
// **** Variables **** //
export const RESERVATION_NOT_FOUND_ERR = 'Réservation non trouvée';
// **** Functions **** //
/**
* Extraire toutes les réservations.
*
* @returns {IReservation[]} Tableau de toutes les réservations
*/
function getAll(): Promise<IReservation[]> {
return ReservationRepo.getAll();
}
/**
* Ajouter une réservation.
*
* @param {IReservation} reservation - Réservation à ajouter
*/
function addOne(reservation: IReservation): Promise<IReservation> {
return ReservationRepo.add(reservation);
}
/**
* Mettre à jour une réservation.
*
* @param {IReservation} reservation - Réservation à mettre à jour
*/
async function updateOne(reservation: IReservation): Promise<void> {
const persists = await ReservationRepo.persists(reservation.id);
if (!persists) {
throw new RouteError(HttpStatusCodes.NOT_FOUND, RESERVATION_NOT_FOUND_ERR);
}
// Retourner la réservation
return ReservationRepo.update(reservation);
}
/**
* Efface une réservation par son ID
*
* @param {number} id - ID de la réservation à supprimer
*/
async function _delete(id: number): Promise<void> {
const persists = await ReservationRepo.persists(id);
if (!persists) {
throw new RouteError(HttpStatusCodes.NOT_FOUND, RESERVATION_NOT_FOUND_ERR);
}
// Efface la réservation
return ReservationRepo.delete(id);
}
// **** Export default **** //
export default {
getAll,
addOne,
updateOne,
delete: _delete,
} as const;
Étape 6 - Créer les routes
routes/ReservationRoute.ts
import HttpStatusCodes from '@src/common/HttpStatusCodes';
import ReservationService from '@src/services/ReservationService';
import { IReservation } from '@src/models/Reservation';
import { IReq, IRes } from './types/express/misc';
// **** Functions **** //
/**
* Extraire toutes les réservations.
*
* @param {IReq} _ - non utilisé
* @param {IRes} res - Réponse du serveur
*
* @returns {string} - Tableau des réservations en JSON
*/
async function getAll(_: IReq, res: IRes) {
const reservations = await ReservationService.getAll();
return res.status(HttpStatusCodes.OK).json({ reservations });
}
/**
* Ajouter une réservation.
*
* @param {IReq} req - Requête au serveur avec une réservation
* @param {IRes} res - Réponse du serveur
*/
async function add(req: IReq<{ reservation: IReservation }>, res: IRes) {
const { reservation } = req.body;
const returnedReservation = await ReservationService.addOne(reservation);
return res
.status(HttpStatusCodes.CREATED)
.json({ reservation: returnedReservation })
.end();
}
/**
* Mettre à jour une réservation.
*
* @param {IReq} req - Requête au serveur avec une réservation
* @param {IRes} res - Réponse du serveur
*/
async function update(req: IReq<{ reservation: IReservation }>, res: IRes) {
const { reservation } = req.body;
await ReservationService.updateOne(reservation);
return res.status(HttpStatusCodes.OK).end();
}
/**
* Supprimer une réservation.
*
* @param {IReq} req - Requête au serveur avec l'id d'une réservation
* @param {IRes} res - Réponse du serveur
*/
async function delete_(req: IReq, res: IRes) {
const id = +req.params.id;
await ReservationService.delete(id);
return res.status(HttpStatusCodes.OK).end();
}
// **** Export default **** //
export default {
getAll,
add,
update,
delete: delete_,
} as const;
Étape 7 - Ajouter les chemins de l’API dans les commons
/common/Paths.ts
/**
* Express router paths go here.
*/
export default {
Base: '/api',
Reservations: {
Base: '/reservations',
Get: '/',
Add: '/',
Update: '/',
Delete: '/delete/:id',
},
} as const;
Étape 8 - Ajouter les chemins de l’API dans index.ts
routes/index.ts
import { Router } from 'express';
import jetValidator from 'jet-validator';
import Paths from '../common/Paths';
import Reservation from '@src/models/Reservation';
import ReservationRoute from './ReservationRoute';
// **** Variables **** //
const apiRouter = Router(),
validate = jetValidator();
// ** Add UserRouter ** //
const reservationRouter = Router();
// Extraire toutes les réservations
reservationRouter.get(Paths.Reservations.Get, ReservationRoute.getAll);
// Ajouter une réservation
reservationRouter.post(
Paths.Reservations.Add,
validate(['reservation', Reservation.isReservation]),
ReservationRoute.add
);
// Mise à jour d'une réservation
reservationRouter.put(
Paths.Reservations.Update,
validate(['reservation', Reservation.isReservation]),
ReservationRoute.update
);
// Supprimer une réservation
reservationRouter.delete(
Paths.Reservations.Delete,
validate(['id', 'number', 'params']),
ReservationRoute.delete
);
// Ajouter le router à l'API
apiRouter.use(Paths.Reservations.Base, reservationRouter);
// **** Export default **** //
export default apiRouter;