fr

NestJS : à quoi sert ce framework, et pourquoi pas juste Fastify ?

Guide complet et sourcé de NestJS : problèmes résolus, concepts et jargon, apport par rapport à Fastify, comparaison Fastify seul vs NestJS sur Fastify, et cas où l'éviter.

Conversation ayant produit cet article

Poser la question « NestJS ou Fastify ? » revient à demander « une charpente ou une scie ? ». Les deux servent à construire, mais pas à la même étape. Fastify est un serveur HTTP : il reçoit une requête, la route, la sérialise vite. NestJS est un framework applicatif qui organise tout ce qu’il y a autour de ce serveur — et qui peut d’ailleurs tourner au-dessus de Fastify.

Cette confusion est fréquente parce que les deux apparaissent dans les mêmes benchmarks et les mêmes tutos « API Node.js ». Cet article clarifie ce que NestJS résout, son vocabulaire, ce qu’il ajoute par-dessus un serveur HTTP nu, et surtout dans quels cas il coûte plus qu’il ne rapporte.

Références utilisées : documentation officielle NestJS (version 11, courante en 2026) et documentation Fastify.


1. À quel problème NestJS répond, et pour qui

Node.js n’impose aucune structure. Avec Express ou Fastify seuls, l’architecture d’une application est entièrement à la charge de l’équipe : où mettre la logique métier, comment câbler les dépendances, comment organiser les dossiers, comment tester. Sur un petit service, c’est une liberté. Sur une application qui grossit avec plusieurs développeurs, c’est une source de divergence : chaque module finit structuré différemment, et l’onboarding devient coûteux.

NestJS répond à ce problème par une structure imposée et opinionée, directement inspirée d’Angular côté back-end : découpage en modules, injection de dépendances, séparation nette contrôleur / service, conventions de nommage. L’objectif affiché est de fournir une architecture « out of the box » qui reste maintenable à grande échelle et testable. Source : NestJS — Introduction.

À destination de qui :

  • Les équipes, plus que les développeurs solo. La valeur de NestJS croît avec le nombre de personnes et la durée de vie du projet : une structure partagée réduit les débats et homogénéise les bases de code.
  • Les développeurs venant du monde Java/Spring ou .NET/C#, qui retrouvent des patterns familiers et un cadre « entreprise » : IoC (Inversion of Control, inversion de contrôle — c’est le framework, pas votre code, qui crée les objets et les fournit à qui en a besoin), DTO (Data Transfer Object — une classe qui décrit la forme d’une donnée qui entre ou sort, servant de contrat de validation), décorateurs et modules.
  • Les applications appelées à durer et à grossir : back-office métier, API de plateforme, systèmes multi-domaines.

NestJS est écrit en TypeScript et le suppose partout : décorateurs, types de DTO, injection par type. On peut faire du JavaScript pur, mais c’est à contre-courant de l’outil.


2. Fastify et NestJS ne sont pas au même étage

C’est le point le plus important de l’article, alors autant le poser tôt avec un schéma.

        Fastify seul                     NestJS
   ┌───────────────────┐        ┌───────────────────────┐
   │ Votre code métier │        │  Modules / Providers   │
   │ + routes Fastify  │        │  Controllers / DI      │  ← couche NestJS
   ├───────────────────┤        ├───────────────────────┤
   │   Serveur HTTP    │        │  Adapter HTTP          │
   │     Fastify       │        │  (Express OU Fastify)  │  ← serveur HTTP
   └───────────────────┘        └───────────────────────┘

Fastify est un serveur HTTP : parsing, routing, cycle requête/réponse, validation de schéma, sérialisation JSON. C’est sa raison d’être et il le fait vite.

NestJS est agnostique du serveur HTTP. Il fonctionne par-dessus un adapter : par défaut Express, mais on peut choisir Fastify via @nestjs/platform-fastify. Source : NestJS — Performance (Fastify).

import { NestFactory } from "@nestjs/core";
import {
  FastifyAdapter,
  NestFastifyApplication,
} from "@nestjs/platform-fastify";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

Autrement dit, la bonne comparaison n’est pas « Fastify ou NestJS » mais « Fastify seul » face à « NestJS (au-dessus d’Express ou de Fastify) ». C’est le fil conducteur des sections 5 et 6.


3. Les concepts principaux et le jargon

NestJS a son vocabulaire. Le comprendre suffit à lire 90 % d’une base de code Nest.

Modules

L’unité d’organisation de base. Un module regroupe des contrôleurs et des providers liés à un domaine (par exemple UsersModule). Chaque application a au moins un module racine. Les modules déclarent ce qu’ils exposent (exports) et ce qu’ils importent des autres (imports), formant un graphe de dépendances explicite.

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

Source : NestJS — Modules.

Controllers

Ils reçoivent les requêtes HTTP et renvoient les réponses. Le routing se déclare par décorateurs (@Controller('cats'), @Get(), @Post()). Un contrôleur ne devrait contenir aucune logique métier — juste l’aiguillage vers les services.

@Controller("cats")
export class CatsController {
  constructor(private catsService: CatsService) {}

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

Source : NestJS — Controllers.

Providers et injection de dépendances (DI / IoC)

C’est le cœur de NestJS. Un provider est n’importe quelle classe annotée @Injectable() — le plus souvent un service qui porte la logique métier. Le conteneur d’inversion de contrôle (IoC) de Nest instancie ces providers et les injecte là où ils sont demandés, par le type déclaré dans le constructeur.

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];
  findAll(): Cat[] {
    return this.cats;
  }
}

Le développeur ne fait jamais new CatsService() : il déclare le besoin, Nest fournit l’instance (singleton par défaut). Ce mécanisme est ce qui rend le code testable — en test, on remplace un provider par un mock via le même conteneur. Source : NestJS — Providers et Custom providers.

Le pipeline de traitement : middleware, guards, interceptors, pipes, filters

NestJS découpe le traitement d’une requête en couches à responsabilité unique, chacune insérable globalement, par contrôleur ou par route :

ÉlémentRôleExemple typique
MiddlewareCode exécuté avant le handler, façon ExpressLogging brut, CORS
GuardAutorise ou refuse l’accès à la routeAuthentification, rôles
InterceptorEnveloppe l’exécution (avant/après)Transformation de réponse, cache, mesure de latence
PipeTransforme et valide les entréesValidation de DTO, parsing de paramètre
Exception filterCapte et met en forme les erreursRéponse d’erreur normalisée

L’ordre du cycle de vie est déterministe : requête → middleware → guards → interceptors (avant) → pipes → handler du contrôleur → service → interceptors (après) → exception filters → réponse. Source : NestJS — Request lifecycle.

Ces éléments s’enregistrent globalement tout en profitant de la DI, via des tokens dédiés :

@Module({
  providers: [{ provide: APP_GUARD, useClass: RolesGuard }],
})
export class AppModule {}

Source : NestJS — Guards.

DTO et décorateurs

Un DTO (Data Transfer Object) est une classe qui décrit la forme d’une entrée. Couplé aux décorateurs de class-validator et au ValidationPipe, il donne une validation déclarative des payloads. Les décorateurs (@Get, @Body, @Injectable, @UseGuards…) sont le langage de NestJS : ils portent les métadonnées que le framework lit à l’exécution.


4. Les caractéristiques notables

Au-delà des concepts de base, quelques traits distinguent NestJS.

  • TypeScript-first. DI par type, DTO typés, décorateurs : l’outil est pensé pour TypeScript. Le typage n’est pas cosmétique, il fait partie du fonctionnement (l’injection résout par le type).
  • Agnostique du transport, pas seulement HTTP. Le même modèle (modules, providers, guards…) sert à écrire des microservices (TCP, Redis, NATS, gRPC, Kafka…), des serveurs GraphQL et des passerelles WebSocket. Guards, interceptors, pipes et filters se réutilisent tels quels entre REST et GraphQL. Sources : NestJS — Microservices, GraphQL.
  • CLI de scaffolding. nest generate crée modules, contrôleurs et services conformes aux conventions, ce qui garde l’homogénéité sans copier-coller. Source : NestJS — CLI.
  • Testabilité intégrée. Le Test.createTestingModule reconstruit le graphe DI en test et permet d’y substituer des providers. Source : NestJS — Testing.
  • Écosystème « batteries incluses ». Modules officiels pour la config, la validation, l’ORM (TypeORM, Prisma, Mongoose), l’authentification (Passport), la doc OpenAPI, le cache, les tâches planifiées, la file d’attente. On assemble des briques prévues pour s’emboîter plutôt que de choisir et câbler chaque librairie soi-même.
  • Applications standalone. Nest peut tourner sans serveur HTTP du tout (CLI, worker), en réutilisant la DI — mais dans ce contexte, middleware / guards / interceptors / pipes ne s’appliquent pas puisqu’il n’y a pas de requête. Source : NestJS — Standalone applications.

5. Ce que NestJS apporte de plus qu’un Fastify seul

Fastify fournit un serveur rapide et quelques primitives (hooks, plugins, décoration d’instance, validation par schéma JSON). Ce qu’il ne fournit pas, et que NestJS ajoute :

  • Une architecture imposée. Modules, séparation contrôleur/service, conventions. Avec Fastify seul, cette structure est à inventer et à maintenir par l’équipe.
  • Un conteneur d’injection de dépendances. Fastify n’a pas de DI par type ; on gère les dépendances à la main ou via fastify.decorate. NestJS offre un vrai conteneur IoC avec scopes, providers personnalisés, injection par token.
  • Un pipeline transversal unifié. Guards, interceptors, pipes et exception filters activables globalement ou par route, avec un ordre garanti. En Fastify, on assemble ça avec des hooks et des plugins, sans cadre unique.
  • Un modèle multi-transport. Le même code passe de REST à GraphQL, microservices ou WebSocket. Fastify est un serveur HTTP ; sortir du HTTP demande d’autres outils.
  • Un écosystème cohérent et une CLI. Modules officiels intégrés au modèle de DI, génération de code conforme.

En une phrase : Fastify optimise le trajet d’une requête ; NestJS organise une application entière. Ce sont des apports orthogonaux, ce qui explique qu’ils coexistent.


6. Fastify seul vs NestJS au-dessus de Fastify

C’est la vraie décision d’architecture. On peut avoir Fastify dans les deux cas — la question est la couche applicative par-dessus.

AspectFastify seulNestJS (adapter Fastify)
Performance HTTP bruteMaximaleFastify + surcoût de la couche Nest (DI, décorateurs)
StructureÀ définir soi-mêmeImposée et homogène
Injection de dépendancesManuelle / decorateConteneur IoC complet
Courbe d’apprentissageFaibleNotable (concepts + jargon)
Boilerplate initialMinimalÉlevé (modules, décorateurs)
Cohérence en équipeDépend de la disciplineGarantie par le framework
Compatibilité écosystèmePlugins Fastify natifsPlugins Fastify-spécifiques requis

Deux points méritent d’être développés.

Performance : Fastify reste Fastify, mais avec un surcoût

La doc NestJS elle-même affiche Fastify comme « quasiment deux fois plus rapide qu’Express » sur ses benchmarks, ce qui motive le choix de l’adapter Fastify quand la performance prime. Source : NestJS — Performance (Fastify).

Mais deux nuances :

  1. Ce gain compare deux adapters de NestJS (Express vs Fastify), pas « NestJS/Fastify » contre « Fastify nu ».
  2. La couche NestJS (résolution DI, décorateurs, pipeline de guards/interceptors) ajoute son propre coût par requête. Un Fastify nu bien écrit sera donc toujours au moins aussi rapide qu’un Nest/Fastify. Pour la grande majorité des API, cet écart est négligeable face à la latence base de données ou réseau — mais il existe, et il compte sur les chemins ultra-chauds.

Piège de compatibilité : l’adapter Fastify n’est pas gratuit

Quand on utilise FastifyAdapter, NestJS opère avec Fastify comme fournisseur HTTP. Les recettes et paquets qui reposent sur Express (middlewares Express, req/res Express, certains modules de la communauté) cessent de fonctionner : il faut leurs équivalents Fastify. Source : NestJS — Performance / Platform specific packages.

En pratique, une part de l’écosystème et de la documentation Nest suppose Express (l’adapter par défaut). Choisir Fastify sous Nest, c’est accepter de vérifier la compatibilité de chaque module et de perdre certaines recettes toutes faites. Le choix « Nest sur Fastify » se justifie donc surtout si la performance HTTP est un critère explicite ; sinon l’adapter Express par défaut est le chemin le mieux balisé.


7. Quand NE PAS utiliser NestJS

NestJS a un coût — apprentissage, boilerplate, surcouche à l’exécution. Voici les cas où ce coût n’est pas amorti.

Petits services, microservices minces et fonctions serverless

Pour un service qui fait une chose (un webhook, un proxy, une petite API à quelques routes), l’appareillage de NestJS est disproportionné. Fastify seul (ou Hono, Express) livre le même résultat avec une fraction du code et un démarrage plus léger. En serverless, le temps de démarrage à froid et la taille du bundle pèsent : la couche de bootstrap de Nest joue contre vous là où un handler minimal excelle.

Prototypes et projets à durée de vie courte

La valeur de NestJS se paie d’avance (structure, concepts) et se récupère plus tard (maintenance, montée d’échelle). Sur un POC ou un projet jetable, on paie l’avance sans toucher le retour.

Équipe non familière de la DI et des décorateurs, sur deadline serrée

NestJS suppose d’être à l’aise avec l’injection de dépendances, les décorateurs et une pensée « à la Angular/Spring ». Une équipe qui découvre ces concepts sous pression produira du code Nest maladroit — souvent pire qu’un Fastify simple et explicite qu’elle maîtrise.

Recherche de performance maximale sur chemin ultra-chaud

Si l’enjeu est de saturer un cœur CPU en requêtes/seconde sur un service dont la logique est triviale (gateway, edge), la surcouche NestJS ne se justifie pas : Fastify nu, voire un serveur encore plus bas niveau, sera plus direct.

Refus de TypeScript

Techniquement NestJS fonctionne en JavaScript, mais l’expérience est dégradée : l’injection par type et les DTO perdent leur intérêt. Sans TypeScript, autant prendre un framework qui ne le présuppose pas.

En pratique, le seuil de bascule est la combinaison taille × durée × taille d’équipe. Une API destinée à grossir, maintenue par plusieurs personnes sur plusieurs années, amortit largement NestJS. Un petit service isolé, temporaire ou tenu par une personne : préférez Fastify seul.


8. Conclusion

  • Fastify est un serveur HTTP ; NestJS est un framework applicatif qui tourne au-dessus d’un serveur (Express par défaut, Fastify en option). La question « l’un ou l’autre » est mal posée.
  • NestJS apporte ce que Fastify ne cherche pas à donner : architecture imposée, injection de dépendances, pipeline transversal unifié, modèle multi-transport, écosystème cohérent — au prix d’une courbe d’apprentissage et d’un surcoût d’exécution.
  • « Nest sur Fastify » garde la vitesse de Fastify moins le coût de la couche Nest, mais oblige à des paquets Fastify-spécifiques et sort du chemin le mieux documenté (Express).
  • Ne prenez pas NestJS pour un petit service, un prototype, du serverless sensible au démarrage à froid, ou une équipe non rôdée à la DI sous deadline. La structure de NestJS se rentabilise sur les applications qui durent, grossissent et sont écrites à plusieurs.

Pour aller plus loin : documentation officielle NestJS, First steps, documentation Fastify.