DUVAL Olivier
Posted on May 8, 2022
TL;DR
Article pour introduire NestJS, framework Typescript backend pour développer des API : fortement inspiré d'Angular, on trouvera des controlers, services, guards et autres décorateurs.
NestJS kesako ?
NestJS est un framework Javascript (plus précisément TypeScript) MVC pour développer un backend au-dessus de NodeJs et Express ou Fastify (qui géront les routes de bas niveau HTTP). Il permet également de développer des API, que nous verrons dans ce billet pour une introduction à NestJs.
La philosophie se rapproche de frameworks backends de type ASPNET WebApi C# / .NET, Java Spring ou Angular côté frontend : approche par couches (controllers, services via de l'injection de dépendances, qui va apporter une couche d'abstraction), par attributs / annotations que l'on appellera décorateurs que l'on retrouve en Angular, modulaire (comme Angular). L'écriture du code s'effectue en TypeScript (mais Javascript reste possible), on retrouvera nos bonnes pratiques de développement (typage, POO, design patterns).
NestJS propose également un CLI nest qui permettra de générer du scaffolding de classes, services, controllers, etc, bon nombre de modules / plugins / mécanismes : des ORM avec par exemple TypeORM ou d'authentification avec PassportJS (et la mécanique technique de gestion de JWT) et d'autorisation : qui a le droit à accéder à telle ou telle API / fonction (Guards avec des rôles, comme sous Angular), de sécurité avec CORS et une prise en charge d'OpenAPI via Swagger pour la documentation de vos APIs.
NestJs d'un point de vue architecture reprend tout ce que l'on connaît déjà de frameworks backend ou frontend de type SPA (particulièrement Angular), en TypeScript, ce qui permet de mutualiser ses connaissances front côté back.
Sommaire
API Livres
Nous allons reprendre dans cette introduction, l'application de gestion de livres de la série Angular / Django REST, en ne faisant que les livres pour l'instant, sans authentification, et sans interface, en axant uniquement sur une API.
Installation de NestJS et initialisation de l'application
$ mkdir library_nest && cd library_nest
Commençons par le CLI, de façon global :
$ npm i -g @nestjs/cli
$ nest --version
8.2.5
Création du squelette de l'application que l'on appellera backend grâce au CLI, je choisis npm
comme gestionnaire de packages, aller dans backend à l'issue du scaffolding
$ nest new backend
⚡ We will scaffold your app in a few seconds..
CREATE backend/.eslintrc.js (665 bytes)
CREATE backend/.prettierrc (51 bytes)
CREATE backend/nest-cli.json (118 bytes)
CREATE backend/package.json (1992 bytes)
CREATE backend/README.md (3340 bytes)
CREATE backend/tsconfig.build.json (97 bytes)
CREATE backend/tsconfig.json (546 bytes)
CREATE backend/src/app.controller.spec.ts (617 bytes)
CREATE backend/src/app.controller.ts (274 bytes)
CREATE backend/src/app.module.ts (249 bytes)
CREATE backend/src/app.service.ts (142 bytes)
CREATE backend/src/main.ts (208 bytes)
CREATE backend/test/app.e2e-spec.ts (630 bytes)
CREATE backend/test/jest-e2e.json (183 bytes)
? Which package manager would you ❤️ to use? npm
🚀 Successfully created project backend
👉 Get started with the following commands:
$ cd backend
On peut lancer le serveur
$ npm run start
ou $ npm run start:dev
pour la prise en compte des modifications de code et du rechargement automatique du serveur (le watcher), en mode développement, c'est plus que recommandé.
La compilation / transpilation en javascript est effectuée dans dist
qu'utilisera le serveur.
On peut accéder à http://localhost:3000/ via un navigateur.
S'il se trouvait un souci avec le port (déjà pris par exemple), il est possible de le modifier dans le fichier backend/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000); // modifier le port ICI
}
bootstrap();
Controller / service / entité
Dans cet article, nous ferons simple, pour démontrer l'architecture controller / service, sans accès à une base de données.
Le déroulement des appels peut être schématisé de la manière suivante :
(source : https://excalidraw.com/#json=RLHChssGR5kaOEKRfNmdI,yIh11YsH_-LOCn11-MJjzA)
Créons avec le CLI quelques squelettes, dans un module book.
Nous aurons dans ce 1er article, un module book dans lequel se trouvera un controller bookControler et un service bookService et une classe entité book. Dans cet article, on n'accédera pas à une base de données, les livres seront juste une liste en dur dans le code.
$ nest g module book # il crée src/book/book.module.ts et met à jour le module principal src/app.module.ts qui le référence
$ nest generate --flat controller book/controller/book # controller en mode flat pour générer les fichiers dans un emplacement choisi, ici book/controller/book, il met aussi à jour le module book.module.ts
$ nest generate --flat service book/service/book # idem mais pour le service, le module book.module.ts est mis à jour
$ nest g --flat class book/book.entity # la classe entité
On a la structure suivante de src :
src
└───book
├───controller
└───service
$ ls -R src | awk '
/:$/&&f{s=$0;f=0}
/:$/&&!f{sub(/:$/,"");s=$0;f=1;next}
NF&&f{ print s"/"$0 }'
src/app.controller.spec.ts
src/app.controller.ts
src/app.module.ts
src/app.service.ts
src/book/
src/main.ts
src/book/book.entity.spec.ts
src/book/book.entity.ts
src/book/book.module.ts
src/book/controller/
src/book/service/
src/book/controller/book.controller.spec.ts
src/book/controller/book.controller.ts
src/book/service/book.service.spec.ts
src/book/service/book.service.ts
Modifications des fichiers pour le rendre utilisable
L'entité book devient :
export class BookEntity {
id: number;
title: string;
}
Le service renvoie une liste en dur de livres ou un livre en particulier par son ID, il sera injecté dans le controller, le mot clé @Injectable
est nécessaire, on initialise quelques livres en dur dans le service :
import { Injectable } from '@nestjs/common';
import { BookEntity } from '../book.entity';
@Injectable()
export class BookService {
books: BookEntity[] = [
<BookEntity>{ id: 1, title: 'NestJS 101' },
<BookEntity>{ id: 2, title: 'Angular' },
];
async getAll(): Promise<BookEntity[]> {
return this.books;
}
async getById(id): Promise<BookEntity> {
return this.books.find((_book: BookEntity) => _book.id === Number(id.id));
}
}
et le controller qui l'utilise, le constructeur, comme en Angular, permet d'injecter le service. Les décorateurs @Controller() et @Get() permettent de définir notre API : sur /api/books
et la méthode GET via @Get()
sur la fonction GetAll() qui renvoie tous les livres ou /api/books/2
GetById() sur un ID particulier.
import { Controller } from '@nestjs/common';
import { BookService } from '../service/book.service';
@Controller('api/books')
export class BookController {
constructor(private bookService: BookService) {}
@Get()
async getAll(): Promise<BookEntity[]> {
return await this.bookService.getAll();
}
@Get(':id')
async getById(@Param() id): Promise<BookEntity> {
return await this.bookService.getById(id);
}
}
Le serveur recharge le tout
[13:31:58] File change detected. Starting incremental compilation...
[13:31:59] Found 0 errors. Watching for file changes.
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [NestFactory] Starting Nest application...
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [InstanceLoader] AppModule dependencies initialized +95ms
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [InstanceLoader] BookModule dependencies initialized +2ms
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [RoutesResolver] BookController {/api/books}: +121ms
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [RouterExplorer] Mapped {/api/books, GET} route +7ms
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [RouterExplorer] Mapped {/api/books/:id, GET} route +2ms
[Nest] 3952 - 08/05/2022, 14:12:11 LOG [NestApplication] Nest application successfully started +4ms
et l'API renvoie bien tous les livres au format JSON
ou un des livres
Conclusion
Cette introduction permet de comprendre l'architecture de base de NestJS, dans des prochains articles, nous nous attaquerons à l'accès à une base de données avec la librairie TypeORM, l'authentification et bien plus !
Retrouvez les sources sur https://github.com/zorky/library_nestjs/tree/nestjs-101 , installer les packages dans backend : $ npm install
et exécuter ! $ npm run start:dev
Posted on May 8, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.