Microservices NestJS en 2026 : Architecture gRPC, Streaming et Questions d'Entretien Technique
Guide pratique sur l'architecture microservices NestJS avec gRPC : couches de transport, Protocol Buffers, patterns de streaming et questions d'entretien pour développeurs backend en 2026.

Les microservices NestJS se sont imposés comme l'approche de référence pour construire des backends Node.js distribués en 2026. Avec la couche de transport améliorée de NestJS 12, le support natif de gRPC, les filtres d'exception pour les appels RPC et la propagation automatique des identifiants de trace, le framework fournit tout le nécessaire pour découper un monolithe en services bien délimités sans sacrifier l'expérience développeur.
gRPC utilise les Protocol Buffers et HTTP/2, offrant une sérialisation jusqu'à 10 fois plus rapide et du streaming bidirectionnel natif par rapport à JSON sur REST. Pour la communication inter-services au sein d'un cluster, gRPC réduit la latence et impose des contrats stricts via les fichiers .proto. REST reste le meilleur choix pour les API publiques consommées par les navigateurs et les tiers.
Architecture de la couche de transport des microservices NestJS
Le package @nestjs/microservices remplace les contrôleurs HTTP par des handlers orientés messages. Au lieu du routage par chemin d'URL, chaque handler répond à un pattern de message, une clé de type chaîne ou objet qui identifie l'opération. Cette abstraction permet au même code de fonctionner avec TCP, Redis, NATS, Kafka, RabbitMQ ou gRPC sans modification.
NestJS 11 a introduit la méthode unwrap() sur tous les transporteurs, donnant un accès direct à l'instance client sous-jacente. Cela résout un problème récurrent : inspecter l'état de connexion, ajuster les intervalles de keepalive ou accéder à des fonctionnalités spécifiques au broker que l'abstraction NestJS n'expose pas. NestJS 12 complète cette fonctionnalité avec les hooks de pré-requête, permettant aux handlers d'exécuter une logique personnalisée juste avant l'invocation pour des préoccupations transversales comme les vérifications d'authentification ou les métriques.
Deux patterns de communication structurent les microservices NestJS :
- Requête-réponse (
@MessagePattern) : le client envoie un message et attend une réponse. Adapté aux requêtes synchrones comme la récupération d'un profil utilisateur ou la validation d'un token. - Événementiel (
@EventPattern) : le client émet un événement et poursuit son exécution, aucune réponse attendue. Idéal pour les journaux d'audit, les notifications ou le déclenchement de workflows en aval.
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';
@Controller()
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
// Request-response: caller waits for the created order
@MessagePattern('order.create')
async createOrder(@Payload() data: CreateOrderDto) {
return this.ordersService.create(data);
}
// Event-based: fire and forget, no response returned
@EventPattern('order.shipped')
async handleOrderShipped(@Payload() data: { orderId: string }) {
await this.ordersService.markAsShipped(data.orderId);
}
}Le handler @MessagePattern sérialise automatiquement la valeur de retour et la renvoie via le transport. Le handler @EventPattern ne renvoie rien : NestJS ignore toute valeur de retour.
À partir de NestJS 12, les transports TCP, Redis, NATS, MQTT et RabbitMQ affichent un avertissement de dépréciation lorsqu'un handler @EventPattern répond à ClientProxy#send(). Ce comportement changera dans la prochaine version majeure, s'alignant sur l'approche existante de Kafka. Il convient de migrer les handlers d'événements vers une sémantique fire-and-forget réelle ou de passer à @MessagePattern pour les flux requête-réponse.
Configuration de gRPC comme transport microservices NestJS
L'intégration gRPC dans NestJS commence par un fichier .proto qui définit le contrat de service. Les Protocol Buffers imposent la sécurité de type au niveau du fil : le client et le serveur doivent s'accorder sur la forme exacte de chaque message avant qu'un seul octet ne transite sur le réseau.
syntax = "proto3";
package users;
service UsersService {
rpc FindOne (UserById) returns (User);
rpc FindMany (UserFilter) returns (stream User);
}
message UserById {
string id = 1;
}
message UserFilter {
string role = 1;
int32 limit = 2;
}
message User {
string id = 1;
string email = 2;
string name = 3;
string role = 4;
}Côté serveur, l'application NestJS démarre un microservice gRPC en pointant vers le fichier proto et en liant une adresse réseau :
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
},
);
await app.listen();
}
bootstrap();Le contrôleur utilise @GrpcMethod au lieu de @MessagePattern. Le décorateur prend le nom du service et le nom de la méthode RPC comme arguments, se mappant directement à la définition proto :
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { UsersService } from './users.service';
@Controller()
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@GrpcMethod('UsersService', 'FindOne')
async findOne(data: { id: string }) {
return this.usersService.findById(data.id);
}
}NestJS gère automatiquement le chargement des proto, la sérialisation et la gestion des connexions HTTP/2. La méthode du contrôleur reçoit des objets TypeScript classiques, aucun décodage proto manuel nécessaire.
Patterns de streaming gRPC dans NestJS
Le streaming serveur convient aux scénarios où une seule requête produit plusieurs résultats dans le temps : flux de prix en temps réel, curseurs de base de données paginés ou mises à jour de progression pendant une tâche longue. Le handler retourne un Observable RxJS, et NestJS transmet chaque valeur émise au client sous forme de message gRPC distinct :
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { GrpcMethod } from '@nestjs/microservices';
@GrpcMethod('UsersService', 'FindMany')
findMany(data: { role: string; limit: number }): Observable<any> {
// Stream users matching the filter one by one
const users$ = from(this.usersService.findByRole(data.role, data.limit));
return users$.pipe(
map((user) => ({
id: user.id,
email: user.email,
name: user.name,
role: user.role,
})),
);
}Le streaming bidirectionnel utilise @GrpcStreamCall(). Le client et le serveur envoient des flux indépendants, utile pour les systèmes de chat, l'édition collaborative ou l'ingestion de télémétrie multiplexée. Le handler reçoit une instance grpc.ServerDuplexStream et gère manuellement le cycle lecture/écriture.
NestJS 12 ajoute la gestion de la contre-pression : les flux de requêtes sont désormais mis en pause pendant que leurs réponses attendent d'être écrites. Cela évite le débordement de buffer lorsqu'un consommateur lent ne peut pas suivre un producteur rapide.
Un Observable qui plante à l'intérieur d'un flux gRPC termine l'appel entier avec un statut CANCELLED. Il est nécessaire d'encapsuler la logique de streaming dans des opérateurs catchError et d'émettre des métadonnées d'erreur adaptées à gRPC. Les erreurs de flux non gérées sont la cause première des coupures de connexion inexpliquées dans les services gRPC NestJS en production.
Filtres d'exception gRPC dans NestJS 12
NestJS 12 introduit une gestion d'exception dédiée pour gRPC, calquée sur le pattern des filtres d'exception HTTP. Les classes d'exception spécifiques aux statuts permettent aux handlers de retourner des codes d'erreur natifs gRPC plutôt que des échecs génériques :
import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { RpcException } from '@nestjs/microservices';
import { status } from '@grpc/grpc-js';
@Catch(RpcException)
export class GrpcExceptionFilter implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
const error = exception.getError() as { code?: number; message?: string };
return throwError(() => ({
code: error.code ?? status.INTERNAL,
message: error.message ?? 'Internal error',
}));
}
}Le filtre peut être appliqué globalement ou par contrôleur pour garantir des réponses d'erreur cohérentes sur tous les endpoints gRPC. Combiné avec la classe RpcException existante, cela apporte une parité de gestion d'erreurs entre les transports HTTP et gRPC.
Applications hybrides : HTTP et gRPC sur le même service
De nombreux services NestJS en production nécessitent à la fois une API REST publique et des endpoints gRPC internes. Le pattern d'application hybride connecte plusieurs transports à une seule instance NestJS :
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
// HTTP server on port 3000
const app = await NestFactory.create(AppModule);
// gRPC microservice on port 5000
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
});
await app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();Cette approche évite de déployer des services séparés lorsqu'un seul contexte délimité nécessite des interfaces externes et internes. Le même conteneur d'injection de dépendances, les mêmes guards, intercepteurs et pipes s'appliquent aux deux transports, réduisant la duplication et maintenant un comportement cohérent.
Prêt à réussir tes entretiens Node.js / NestJS ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Patterns Regexp Kafka dans NestJS 12
NestJS 12 permet à @MessagePattern() et @EventPattern() d'accepter des expressions régulières pour les topics Kafka. Un seul handler peut désormais s'abonner dynamiquement à plusieurs topics :
import { Controller } from '@nestjs/common';
import { EventPattern, Payload, Ctx, KafkaContext } from '@nestjs/microservices';
@Controller()
export class AnalyticsController {
// Matches analytics.pageview, analytics.click, analytics.purchase, etc.
@EventPattern(/^analytics...*/)
handleAnalyticsEvent(
@Payload() data: Record<string, unknown>,
@Ctx() context: KafkaContext,
) {
const topic = context.getTopic();
console.log(`Received event on ${topic}`, data);
}
}Cela réduit le code répétitif lorsque plusieurs topics partagent la même logique de traitement. La validation des clés de pattern dans NestJS 12 impose un maximum de 256 caractères : les patterns plus longs échouent silencieusement et ne correspondent à aucun handler.
Migration NATS v3 dans NestJS 12
NestJS 12 remplace le client NATS par le package officiel @nats-io/transport-node. C'est un changement majeur : les désérialiseurs reçoivent désormais l'objet message complet au lieu du payload brut.
// Before (NestJS 11)
deserialize(value: any): any {
return JSON.parse(value);
}
// After (NestJS 12): read payload via msg.json()
deserialize(msg: any): any {
return msg.json();
}Tous les désérialiseurs NATS personnalisés doivent être mis à jour lors de la migration vers NestJS 12. Le nouveau client apporte des performances améliorées et un alignement avec l'écosystème NATS officiel.
Limites de services et conception pilotée par le domaine avec NestJS
Découper un monolithe en microservices sans limites claires crée un monolithe distribué, le pire des deux architectures. Chaque microservice NestJS doit posséder un seul contexte délimité : sa propre base de données, ses propres modèles de domaine et son propre cycle de déploiement.
Principes pratiques pour définir les limites de services dans NestJS :
- Un module NestJS par racine d'agrégat. Si
OrderetOrderLinechangent toujours ensemble, ils appartiennent au même service. SiUseretOrderchangent indépendamment, ils méritent des services séparés. - Des événements asynchrones pour les effets de bord inter-domaines. Lorsqu'une commande est passée, un événement
order.createdest émis via Kafka ou RabbitMQ. Le service d'inventaire réagit sans que le service de commandes ne connaisse son existence. - Des packages proto partagés pour les contrats. Les fichiers
.protosont stockés dans un dépôt partagé. Le producteur et le consommateur génèrent les types depuis la même source de vérité, la dérive devient un échec CI, pas un incident en production.
Les options microservices de NestJS 12 depuis le conteneur DI rendent la configuration des services testable. Au lieu de coder en dur les URLs des brokers dans main.ts, il suffit d'injecter un ConfigService et de changer les chaînes de connexion par environnement sans reconstruire.
Patterns de fiabilité : Timeouts, Retries et Circuit Breakers
Les appels gRPC internes sans deadline transforment une seule dépendance lente en panne système généralisée. Chaque appel ClientProxy.send() doit inclure un timeout :
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { firstValueFrom, timeout, retry } from 'rxjs';
@Injectable()
export class OrdersService {
private usersService: any;
constructor(@Inject('USERS_PACKAGE') private client: ClientGrpc) {}
onModuleInit() {
this.usersService = this.client.getService('UsersService');
}
async getOrderWithUser(orderId: string, userId: string) {
// 3-second deadline, 2 retries with exponential backoff
const user = await firstValueFrom(
this.usersService.findOne({ id: userId }).pipe(
timeout(3000),
retry({ count: 2, delay: (err, retryCount) => {
const jitter = Math.random() * 100;
return new Promise(r => setTimeout(r, 1000 * retryCount + jitter));
}}),
),
);
return { orderId, user };
}
}Les retries ne doivent s'appliquer qu'aux opérations idempotentes. Rejouer un appel CreateOrder risque de créer des commandes en double. Rejouer une requête FindOne est sans danger car elle n'a aucun effet de bord.
Pour la fonctionnalité de circuit breaker, la bibliothèque opossum s'intègre bien avec NestJS. Il suffit d'encapsuler les appels ClientGrpc dans un circuit breaker qui s'ouvre après N échecs consécutifs et se referme après un timeout de réinitialisation configurable.
NestJS 12 poursuit la propagation automatique des identifiants de trace introduite dans NestJS 11 à travers les transports Kafka, RabbitMQ et gRPC. Combiné avec OpenTelemetry, cela permet un traçage distribué complet sans intercepteurs personnalisés.
Questions d'entretien technique sur les microservices NestJS
Les entretiens techniques pour les postes backend testent de plus en plus les connaissances en microservices aux côtés des patterns spécifiques au framework. Ces questions couvrent les concepts les plus fréquemment posés lors des entretiens orientés NestJS.
Quelle est la différence entre @MessagePattern et @EventPattern ?
@MessagePattern implémente le pattern requête-réponse : le client envoie un message et bloque jusqu'à recevoir une réponse. @EventPattern implémente le pattern fire-and-forget : le client émet un événement et poursuit immédiatement son exécution. En interne, @MessagePattern retourne le résultat du handler via le transport, tandis que @EventPattern ignore les valeurs de retour. NestJS 12 a déprécié la réponse depuis les handlers @EventPattern sur la plupart des transports.
Comment NestJS charge-t-il et lie-t-il les fichiers .proto pour gRPC ?
NestJS utilise le package @grpc/proto-loader pour parser les fichiers .proto au démarrage et générer les descripteurs de service. La configuration GrpcOptions spécifie le chemin du proto et le nom du package. Les contrôleurs annotés avec @GrpcMethod('ServiceName', 'MethodName') sont associés aux descripteurs parsés. Si une méthode déclarée dans le proto n'a pas de handler correspondant, NestJS lève une erreur au démarrage.
Quand gRPC doit-il remplacer REST entre microservices ?
gRPC est adapté aux appels internes entre services où la sécurité de type, la performance et le streaming importent. Les Protocol Buffers produisent des payloads plus compacts et une sérialisation plus rapide que JSON. Le multiplexage HTTP/2 réduit la surcharge de connexion. REST reste préférable pour les API publiques, les clients navigateur et les intégrations tierces où la lisibilité humaine et le large support d'outillage l'emportent sur les performances brutes.
Comment fonctionne le pattern d'application hybride ?
NestFactory.create() démarre un serveur HTTP. L'appel à app.connectMicroservice() attache des transports supplémentaires, gRPC, Kafka, Redis ou tout transporteur supporté, à la même instance NestJS. Tous les transports partagent le même arbre de modules, le même conteneur DI et le même pipeline de middleware. app.startAllMicroservices() démarre tous les transporteurs connectés, et app.listen() démarre la couche HTTP.
Quels patterns de fiabilité préviennent les pannes en cascade ?
Trois patterns sont essentiels : les timeouts (chaque appel RPC nécessite une deadline), les retries avec jitter (uniquement pour les opérations idempotentes, avec backoff exponentiel plus jitter aléatoire pour éviter les effets de troupeau), et les circuit breakers (ouverts après N échecs, semi-ouverts après un délai de refroidissement, fermés une fois les vérifications de santé réussies). L'intégration RxJS de NestJS rend les timeouts et retries composables via les opérateurs pipe.
Quelles sont les nouveautés pour les microservices dans NestJS 12 ?
NestJS 12 a ajouté les filtres d'exception gRPC pour une gestion d'erreurs spécifique aux statuts, les patterns regexp Kafka pour les handlers multi-topics, le support NATS v3 avec un nouveau package client, les hooks de pré-requête pour les handlers de microservices, et une meilleure gestion de la contre-pression pour les streams gRPC. L'avertissement de dépréciation @EventPattern signale un changement comportemental futur où les handlers d'événements ne répondront plus aux appels send().
Pour approfondir les patterns d'architecture NestJS, le module modules et injection de dépendances NestJS couvre les fondamentaux DI qui sous-tendent la composition des microservices. Le module middleware et intercepteurs aborde les préoccupations transversales partagées entre les transports. Le guide NestJS et GraphQL présente une alternative à gRPC pour les API de services typées.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Sources
- NestJS v12 is Now Available — annonce officielle couvrant les filtres d'exception gRPC, les patterns regexp Kafka, NATS v3, les hooks de pré-requête
- NestJS Releases — changelog incluant la contre-pression des streams gRPC, les limites de clés de pattern, la dépréciation d'EventPattern
- NestJS Hybrid Application Documentation — guide officiel pour combiner les transports HTTP et microservices
Points clés pour les microservices NestJS en 2026
- L'abstraction de transport de NestJS 12 permet aux services de basculer entre TCP, gRPC, Kafka et NATS sans réécrire la logique des handlers : les décorateurs
@MessagePatternet@EventPatternfonctionnent de manière identique sur tous les transports - gRPC avec les Protocol Buffers impose des contrats stricts entre services via les fichiers
.proto, détectant les erreurs d'intégration à la compilation plutôt qu'en production - Les applications hybrides combinent HTTP et gRPC sur la même instance NestJS, éliminant le besoin de déployer des services séparés pour les interfaces publiques et internes
- Le streaming serveur via les Observables RxJS permet des flux de données en temps réel sans la complexité des WebSockets, et NestJS 12 ajoute la gestion de la contre-pression pour les consommateurs lents
- Chaque appel RPC interne nécessite une deadline, les retries ne s'appliquent qu'aux opérations idempotentes, et les circuit breakers empêchent un seul service lent de provoquer une panne en cascade
- Les limites de services suivent la conception pilotée par le domaine : un contexte délimité par service, des événements asynchrones pour la communication inter-domaines, des packages proto partagés pour l'application des contrats
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Tu saurais repérer le bug en Node.js / NestJS ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 22 octobre 2026
Tags
Partager
Articles similaires

NestJS + Prisma : La stack moderne pour le backend Node.js
Guide complet pour créer une API backend moderne avec NestJS et Prisma. Configuration, modèles, services, transactions et bonnes pratiques expliquées.

NestJS : Créer une API REST complète
Guide complet pour créer une API REST professionnelle avec NestJS. Controllers, Services, Modules, validation avec class-validator et gestion des erreurs expliqués.

NestJS et MongoDB en 2026 : Mongoose, Agrégations et Questions d'Entretien
Guide complet sur l'intégration de NestJS 12 avec MongoDB via Mongoose 9. Découvrez les schémas, les pipelines d'agrégation et les questions techniques fréquentes en entretien.