DEV Community

Cover image for NestJS 101
DUVAL Olivier
DUVAL Olivier

Posted on • Updated on

NestJS 101

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érons 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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();
Enter fullscreen mode Exit fullscreen mode

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 :

Image description

(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é
Enter fullscreen mode Exit fullscreen mode

On a la structure suivante de src :

src
└───book
    ├───controller
    └───service
Enter fullscreen mode Exit fullscreen mode
$ 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
Enter fullscreen mode Exit fullscreen mode

Modifications des fichiers pour le rendre utilisable

L'entité book devient :

export class BookEntity {
  id: number;
  title: string;
}
Enter fullscreen mode Exit fullscreen mode

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));
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

et l'API renvoie bien tous les livres au format JSON

Image description

ou un des livres

Image description

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

Discussion (0)