NestJS 101
Tags: nestjs, backend, mvc, api
Published: 2022-05-08 12:25:40

**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 ? <a name="intro"></a>

[NestJS](https://nestjs.com/) est un framework **Javascript** (plus précisément TypeScript) **MVC** pour développer un **backend** au-dessus de [NodeJs](https://nodejs.org/) et [Express](https://expressjs.com/fr/) ou [Fastify](https://www.fastify.io/) (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](https://docs.nestjs.com/cli/overview)** 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](https://typeorm.io/entities) ou d'[authentification](https://docs.nestjs.com/security/authentication) 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](https://docs.nestjs.com/guards) avec des [rôles](https://docs.nestjs.com/security/authorization), comme sous Angular), de sécurité avec [CORS](https://docs.nestjs.com/security/cors) et une prise en charge d'[OpenAPI](https://docs.nestjs.com/openapi/introduction) 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
  * [NestJS](#intro)
  * [API livres](#books)
    * [Initialisation] (#installation-de-nestjs-et-initialisation-de-lapplication)
    * [Squelettes controller / service / entité] (#controllerservice)
    * [Modification des squelettes] (#modificationcode)
  * [Conclusion](#conclusion)


# API Livres <a name="books"></a>

Nous allons reprendre dans cette introduction, l'application de gestion de livres de la série [Angular / Django REST](https://dev.to/zorky/django-drf-101-partie-1-2p2), 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 <a name="intro"></a>

`$ mkdir library_nest && cd library_nest`

Commençons par le CLI, de façon global :

`$ npm i -g @nestjs/cli`

```bash
$ 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

```bash
$ 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. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bsnaph4i4azqgy4bzr5.png)

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`

```typescript
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é <a name="controllerservice"></a>

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94ackwgedz9ik8axh59z.png)
 

(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.

```bash
$ 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** :

```bash
src
└───book
    ├───controller
    └───service
```

```bash
$ 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 <a name="modificationcode"></a>

L'entité book devient :

```typescript
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 :

```typescript
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.

```typescript
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 

```bash
[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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b5rjrsyc7cz82g4mk98a.png)

ou un des livres


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oicbqks8p6ajkqnnv4gh.png)

## Conclusion <a name="conclusion"></a>

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`