Structurez et sécurisez votre API avec NestJS

Dans le chapitre précédent, vous avez accompli une étape cruciale : vous avez créé votre première route et récupéré des données grâce à Postman. Votre application commence à prendre forme ! Cependant, une API qui fonctionne techniquement n'est pas forcément une API prête pour le monde réel.

Comment s'assurer que n'importe qui ne puisse pas supprimer mes données ou faire planter mon serveur avec des requêtes malveillantes ?

C'est ce que nous allons voir en découvrant les outils de sécurisation de NestJS.

Comprenez les limites d'une API simple

Jusqu'à présent, notre route/projectsfonctionne parfaitement. Mais avez-vous remarqué un détail préoccupant ? Absolument tout le monde peut y accéder. Il suffit de connaître l'URL pour récupérer la liste de nos projets.

Dans un contexte professionnel, c'est impensable. Certaines routes doivent être réservées aux administrateurs, d'autres nécessitent que l'utilisateur soit connecté, et d'autres encore attendent des données dans un format très précis. Si nous laissons notre API en l'état, nous nous exposons à une absence totale de validation et à un accès non sécurisé.

Pourquoi je ne ferais pas simplement ces vérifications directement dans mon contrôleur, juste avant d'appeler le service ?

C'est une erreur classique ! Si vous vérifiez l'identité de l'utilisateur ou le format des données directement dans le Controller, vous allez devoir dupliquer ce code sur absolument toutes vos routes. En plus d'être fastidieux, cela brise le principe de séparation des responsabilités. Le Controller doit uniquement aiguiller les requêtes, pas faire le travail d'un agent de sécurité. C'est pourquoi NestJS impose l'utilisation d'un pipeline structuré.

Comparaison entre une API simple et une API robuste, montrant l’ajout d’étapes de validation et de sécurité avant le contrôleur et le service pour garantir une réponse fiable.
Une API robuste ajoute des étapes de validation et de sécurité avant le traitement

Comprenez le pipeline de traitement d'une requête

Pour pallier ce problème de duplication, NestJS met en place une chaîne de traitement, aussi appelée pipeline, que chaque requête entrante va devoir traverser.

On peut faire le parallèle avec l'accès à un bâtiment ultra-sécurisé.

  1. Le Guard (contrôle d'accès) : C'est le vigile à l'entrée du bâtiment. Il regarde votre badge et décide si vous avez le droit d'entrer ou non.

  2. Le Pipe (vérification et transformation) : C'est le portique de sécurité. Il vérifie que ce que vous avez dans vos poches est conforme aux règles de l'entreprise (validation) et peut même transformer certains éléments pour qu'ils soient acceptables.

  3. Le Controller : C'est l'accueil, qui comprend ce que vous venez faire et vous indique le bon bureau.

  4. Le Service : C'est l'expert métier qui va traiter votre demande.

Le cheminement complet d'une requête dans NestJS est donc : Requête HTTP → Guards → Pipes → Controller → Service. En respectant scrupuleusement cet ordre et en séparant les responsabilités, votre code devient extrêmement robuste.

Schéma du flux d’une API : requête, contrôle d’accès (guards), validation (pipe), traitement par le contrôleur, logique métier dans le service, puis envoi d’une réponse validée.
NestJS applique des couches de contrôle avant de traiter la requête

Protégez vos routes avec des guards

Regardons de plus près notre fameux vigile : leGuard. Son unique objectif est de contrôler l'accès à une route. Il s'exécute toujours avant le Controller et renvoie une réponse binaire : il bloque la requête ou il l'autorise.

Très souvent, les Guards sont utilisés pour gérer l'authentification. Par exemple, ils vont vérifier la présence d'un jeton JWT (JSON Web Token) dans la requête de l'utilisateur. Bien que nous n'implémenterons pas de JWT complet tout de suite, voici à quoi ressemble l'utilisation d'un Guard dans votre code, grâce à un simple décorateur@UseGuards():

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard'; // Un guard que nous aurions créé
@Controller('projects')
export class ProjectsController {
  @Get()
  @UseGuards(AuthGuard)
  getProjects() {
    return 'Cette route est maintenant protégée !';
  }
}

Désormais, si le GuardAuthGuardéchoue, la requête n'atteindra jamais le Controller.

Schéma montrant une requête passant par une validation (gate) : si valide, elle est autorisée vers le contrôleur, sinon elle est bloquée et refusée.
Le Guard vérifie la requête et décide si elle peut continuer ou être bloquée

Validez les données avec des pipes

Passons maintenant à la deuxième étape de notre pipeline : les Pipes. Eux aussi se déclenchent avant le Controller, mais leur mission est de valider et de transformer les données entrantes.

Concrètement, imaginez que vous vouliez récupérer un seul projet en fonction de son identifiant, via une route dynamique comme/projects/1. En HTTP, tous les paramètres envoyés dans l'URL sont du texte (des chaînes de caractères, ou strings). Pourtant, dans notre code TypeScript, nous voulons manipuler un nombre (number).

Au lieu de faire la conversion manuellement, nous utilisons un Pipe natif de NestJS appeléParseIntPipe.

import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('projects')
export class ProjectsController {
  @Get(':id')
  getOneProject(@Param('id', ParseIntPipe) id: number) {
    return `Le projet demandé porte l'identifiant ${id}`;
  }
}
Schéma montrant une requête passant par une validation (pipe) : si valide, elle est autorisée vers le contrôleur, sinon elle est bloquée et refusée.
Le Pipe valide les données avant leur traitement par le controller

L'avantage est double. Premièrement, le Pipe transforme automatiquement la string en number. Deuxièmement, si l'utilisateur tente d'accéder à/projects/abc, le Pipe va détecter que "abc" n'est pas un nombre. Il va automatiquement rejeter la requête avec une erreur appropriée, évitant ainsi à notre Controller de s'en soucier.

Faites le lien avec Express

Si vous avez déjà de l'expérience avec Node.js, ce nouveau vocabulaire peut sembler perturbant. Pourquoi inventer les Guards et les Pipes alors que les frameworks minimalistes comme Express utilisent simplement des "middlewares" pour tout faire ?

C'est justement pour éviter le chaos. Express vous donne un couteau suisse (le middleware) et vous laisse l'utiliser pour tout : l'authentification, la validation, le traitement de la requête. Résultat : la logique finit souvent dispersée et difficile à maintenir.

Voici comment faire le parallèle :

  • En Express, on utilise des Middlewares à tout faire. Dans NestJS, on utilise des Guards (pour la sécurité) et des Pipes (pour la validation).

  • En Express, on a des Routes directes. Dans NestJS, on utilise des Controllers.

  • En Express, la logique est souvent dispersée. Dans NestJS, l'architecture modulaire garantit que chaque chose est à sa place.

Cette structure claire et opinionnée remplace et améliore nettement les pratiques de base.

À vous de jouer !

L’API de la startup commence à prendre forme. Votre tech lead souhaite maintenant vous faire découvrir un mécanisme essentiel de NestJS : les guards.

Leur rôle est de décider si une requête a le droit d’accéder à une route. Pour cette première mise en pratique, vous allez créer un faux guard afin d’observer facilement son comportement.

Consignes

À partir du projet NestJS créé dans les activités précédentes, réalisez les actions suivantes :

  1. Créez un guard nomméRolesGuard.

  2. Configurez ce guard pour qu’il retournetrueafin d’autoriser toutes les requêtes.

  3. Appliquez ce guard à une route existante de votre application.

  4. Testez la route dans Postman (ou Thunder Client) et vérifiez que la requête aboutit bien.

  5. Modifiez ensuite le guard pour qu’il retournefalse.

  6. Testez à nouveau la même route et observez la différence de comportement.

L’objectif est de comprendre qu’un guard peut autoriser ou bloquer l’accès à une route avant même l’exécution de la logique métier.

En résumé

  • Une API robuste ne se limite pas à des routes fonctionnelles : elle doit être sécurisée et valider les données.

  • Les guards permettent de contrôler l'accès aux routes en bloquant ou en autorisant une requête entrante.

  • Les pipes s'assurent de valider et de transformer les données avant qu'elles n'atteignent votre contrôleur.

  • Le framework NestJS structure le traitement des requêtes à travers un pipeline clair, strict et modulaire.

  • Ces mécanismes de séparation des responsabilités remplacent et améliorent les pratiques globales souvent vues avec Express.

Vous comprenez à présent comment sécuriser l'accès et valider les données de votre application grâce aux Guards et aux Pipes. Il ne reste qu’à tester vos connaissances avec le quiz. Bonne chance !

Ever considered an OpenClassrooms diploma?
  • Up to 100% of your training program funded
  • Flexible start date
  • Career-focused projects
  • Individual mentoring
Find the training program and funding option that suits you best