Microservizi con NestJS nel 2026: Architettura, gRPC e Domande da Colloquio

Guida pratica all'architettura microservizi NestJS con gRPC, che copre confini dei servizi, layer di trasporto, pattern di streaming e domande comuni da colloquio per il 2026.

Diagramma architetturale di microservizi NestJS con gRPC e pattern di comunicazione

I microservizi NestJS sono diventati l'approccio standard per la costruzione di backend Node.js distribuiti nel 2026. Con il layer transporter migliorato di NestJS 12, il supporto gRPC di prima classe, i filtri exception per chiamate RPC e la propagazione automatica dei trace ID, il framework fornisce tutto il necessario per suddividere un monolite in servizi ben delimitati senza sacrificare l'esperienza di sviluppo.

gRPC vs REST per servizi interni

gRPC utilizza Protocol Buffers e HTTP/2, offrendo una serializzazione fino a 10 volte piu veloce e streaming bidirezionale nativo rispetto a JSON su REST. Per la comunicazione service-to-service all'interno di un cluster, gRPC riduce la latenza e impone contratti rigorosi tramite file .proto. REST rimane la scelta migliore per API pubbliche consumate da browser e terze parti.

Architettura del Transport Layer nei Microservizi NestJS

Il pacchetto @nestjs/microservices sostituisce i controller HTTP con handler orientati ai messaggi. Invece di instradare per percorso URL, ogni handler risponde a un message pattern, una stringa o chiave oggetto che identifica l'operazione. Questa astrazione significa che la stessa logica handler funziona su TCP, Redis, NATS, Kafka, RabbitMQ o gRPC senza modifiche al codice.

NestJS 11 ha introdotto il metodo unwrap() su tutti i transporter, garantendo accesso diretto all'istanza client sottostante. Questo risolve un problema di lunga data: ispezionare lo stato della connessione, regolare intervalli keepalive o accedere a funzionalita specifiche del broker che l'astrazione NestJS non espone. NestJS 12 estende questo con pre-request hooks, permettendo agli handler di eseguire logica personalizzata immediatamente prima dell'invocazione per aspetti trasversali come controlli di autenticazione o metriche.

Due pattern di comunicazione guidano i microservizi NestJS:

  • Request-response (@MessagePattern): il client invia un messaggio e attende una risposta. Adatto per query sincrone come recuperare un profilo utente o validare un token.
  • Event-based (@EventPattern): il client emette un evento e prosegue, nessuna risposta attesa. Ideale per audit log, notifiche o attivazione di workflow downstream.
orders.controller.tstypescript
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: il chiamante attende l'ordine creato
  @MessagePattern('order.create')
  async createOrder(@Payload() data: CreateOrderDto) {
    return this.ordersService.create(data);
  }

  // Event-based: fire and forget, nessuna risposta
  @EventPattern('order.shipped')
  async handleOrderShipped(@Payload() data: { orderId: string }) {
    await this.ordersService.markAsShipped(data.orderId);
  }
}

L'handler @MessagePattern serializza automaticamente il valore di ritorno e lo reinvia attraverso il trasporto. L'handler @EventPattern non restituisce nulla: NestJS scarta qualsiasi valore di ritorno.

Deprecazione EventPattern in NestJS 12

A partire da NestJS 12, i transporter TCP, Redis, NATS, MQTT e RabbitMQ registrano un avviso di deprecazione quando un handler @EventPattern risponde a ClientProxy#send(). Questo comportamento cambiera nella prossima versione major, allineandosi all'approccio gia esistente di Kafka. Migrare gli event handler a semantica fire-and-forget autentica oppure passare a @MessagePattern per flussi request-response.

Configurare gRPC come Trasporto Microservizi NestJS

L'integrazione gRPC in NestJS inizia con un file .proto che definisce il contratto del servizio. Protocol Buffers impone la type safety a livello wire: sia client che server devono concordare sulla forma esatta di ogni messaggio prima che un singolo byte attraversi la rete.

proto/users.protoprotobuf
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;
}

Lato server, l'applicazione NestJS avvia un microservizio gRPC puntando al file proto e associando un indirizzo di rete:

main.tstypescript
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();

Il controller usa @GrpcMethod invece di @MessagePattern. Il decorator accetta il nome del servizio e il nome del metodo RPC come argomenti, mappando direttamente alla definizione proto:

users.controller.tstypescript
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 gestisce automaticamente il caricamento dei proto, la serializzazione e la gestione delle connessioni HTTP/2. Il metodo controller riceve oggetti TypeScript plain, nessuna decodifica proto manuale necessaria.

Pattern di Streaming gRPC in NestJS

Il server streaming si adatta a scenari dove una singola richiesta produce risultati multipli nel tempo: feed di prezzi in tempo reale, cursori database paginati o aggiornamenti di progresso durante un task a lunga esecuzione. L'handler restituisce un Observable RxJS, e NestJS invia in streaming ogni valore emesso al client come messaggio gRPC separato:

users.controller.ts (server streaming)typescript
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> {
  // Invia in stream gli utenti che corrispondono al filtro uno per uno
  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,
    })),
  );
}

Lo streaming bidirezionale usa @GrpcStreamCall(). Sia client che server inviano stream indipendenti, utile per sistemi di chat, editing collaborativo o ingestione telemetria multiplexata. L'handler riceve un'istanza grpc.ServerDuplexStream e gestisce manualmente il ciclo read/write.

NestJS 12 aggiunge la gestione della backpressure: i request stream ora si mettono in pausa mentre le loro risposte attendono di essere scritte. Questo previene l'overflow del buffer quando un consumer lento non riesce a tenere il passo con un producer veloce.

Gestione errori nello streaming

Un Observable che crasha all'interno di uno stream gRPC termina l'intera chiamata con status CANCELLED. Wrappare la logica di stream in operatori catchError ed emettere metadata di errore appropriati per gRPC. Gli errori di stream non gestiti sono la causa principale di disconnessioni misteriose nei servizi NestJS gRPC in produzione.

Filtri Exception gRPC in NestJS 12

NestJS 12 introduce la gestione dedicata delle exception per gRPC, rispecchiando il familiare pattern exception filter HTTP. Le classi exception specifiche per status permettono agli handler di restituire codici di errore nativi gRPC invece di fallimenti generici:

grpc-exception.filter.tstypescript
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',
    }));
  }
}

Il filtro puo essere applicato globalmente o per controller per garantire risposte di errore consistenti su tutti gli endpoint gRPC. Combinato con la classe RpcException esistente, questo porta la parita della gestione errori tra trasporti HTTP e gRPC.

Applicazioni Ibride: HTTP e gRPC sullo Stesso Servizio

Molti servizi NestJS reali necessitano sia di un'API REST pubblica che di endpoint gRPC interni. Il pattern hybrid application connette trasporti multipli a una singola istanza NestJS:

main.ts (hybrid application)typescript
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  // Server HTTP sulla porta 3000
  const app = await NestFactory.create(AppModule);

  // Microservizio gRPC sulla porta 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();

Questo approccio evita il deploy di servizi separati quando un singolo bounded context necessita sia di interfacce esterne che interne. Lo stesso container di dependency injection, guard, interceptor e pipe si applicano a entrambi i trasporti, riducendo la duplicazione e mantenendo il comportamento consistente.

Pronto a superare i tuoi colloqui su Node.js / NestJS?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Pattern Regexp Kafka in NestJS 12

NestJS 12 permette a @MessagePattern() e @EventPattern() di accettare espressioni regolari per topic Kafka. Un singolo handler puo ora sottoscrivere dinamicamente topic multipli:

multi-topic.controller.tstypescript
import { Controller } from '@nestjs/common';
import { EventPattern, Payload, Ctx, KafkaContext } from '@nestjs/microservices';

@Controller()
export class AnalyticsController {
  // Matcha analytics.pageview, analytics.click, analytics.purchase, etc.
  @EventPattern(/^analytics..*/)
  handleAnalyticsEvent(
    @Payload() data: Record<string, unknown>,
    @Ctx() context: KafkaContext,
  ) {
    const topic = context.getTopic();
    console.log(`Evento ricevuto su ${topic}`, data);
  }
}

Questo riduce il boilerplate quando topic multipli condividono la stessa logica handler. La validazione della pattern key in NestJS 12 impone un massimo di 256 caratteri: pattern piu lunghi falliscono silenziosamente e non matchano nessun handler.

Migrazione NATS v3 in NestJS 12

NestJS 12 sostituisce il client NATS con il pacchetto ufficiale @nats-io/transport-node. Questo e un breaking change: i deserializer ora ricevono l'oggetto messaggio completo invece del payload grezzo.

typescript
// Prima (NestJS 11)
deserialize(value: any): any {
  return JSON.parse(value);
}

// Dopo (NestJS 12): leggere il payload via msg.json()
deserialize(msg: any): any {
  return msg.json();
}

Aggiornare tutti i deserializer NATS personalizzati durante la migrazione a NestJS 12. Il nuovo client porta prestazioni migliorate e allineamento con l'ecosistema NATS ufficiale.

Confini dei Servizi e Domain-Driven Design con NestJS

Dividere un monolite in microservizi senza confini chiari crea un monolite distribuito, il peggio di entrambe le architetture. Ogni microservizio NestJS dovrebbe possedere un singolo bounded context: il proprio database, i propri modelli di dominio e il proprio ciclo di vita di deployment.

Linee guida pratiche per definire i confini dei servizi in NestJS:

  • Un modulo NestJS per aggregate root. Se Order e OrderLine cambiano sempre insieme, appartengono allo stesso servizio. Se User e Order cambiano indipendentemente, meritano servizi separati.
  • Eventi asincroni per side effect cross-domain. Quando un ordine viene effettuato, emettere un evento order.created via Kafka o RabbitMQ. Il servizio inventory reagisce senza che il servizio ordini sappia della sua esistenza.
  • Pacchetti proto condivisi per i contratti. Memorizzare i file .proto in un repository condiviso. Sia producer che consumer generano tipi dalla stessa source of truth, cosi il drift diventa un fallimento CI, non un incidente in produzione.

Le opzioni microservizi di NestJS 12 dal container DI rendono la configurazione del servizio testabile. Invece di hardcodare URL broker in main.ts, iniettare un ConfigService e scambiare le connection string per ambiente senza ricompilare.

Pattern di Affidabilita: Timeout, Retry e Circuit Breaker

Le chiamate gRPC interne senza deadline trasformano una singola dipendenza lenta in un'interruzione a livello di sistema. Ogni chiamata ClientProxy.send() dovrebbe includere un timeout:

orders.service.tstypescript
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) {
    // Deadline 3 secondi, 2 retry con backoff esponenziale
    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 };
  }
}

I retry dovrebbero applicarsi solo a operazioni idempotenti. Ritentare una chiamata CreateOrder rischia ordini duplicati. Ritentare una query FindOne e sicuro perche non ha side effect.

Per la funzionalita circuit breaker, la libreria opossum si integra bene con NestJS. Wrappare le chiamate ClientGrpc in un circuit breaker che apre dopo N fallimenti consecutivi e chiude dopo un timeout di reset configurabile.

Propagazione trace NestJS 12

NestJS 12 continua la propagazione automatica dei trace ID introdotta in NestJS 11 sui trasporti Kafka, RabbitMQ e gRPC. Combinato con OpenTelemetry, questo abilita il tracing distribuito completo senza interceptor personalizzati.

Domande da Colloquio sui Microservizi NestJS

I colloqui tecnici per posizioni backend testano sempre piu spesso la conoscenza dei microservizi insieme ai pattern specifici del framework. Queste domande coprono i concetti piu frequentemente richiesti nei colloqui focalizzati su NestJS.

Qual e la differenza tra @MessagePattern e @EventPattern?

@MessagePattern implementa request-response: il client invia un messaggio e si blocca finche non riceve una risposta. @EventPattern implementa fire-and-forget: il client emette un evento e continua immediatamente l'esecuzione. Internamente, @MessagePattern restituisce il risultato dell'handler attraverso il trasporto, mentre @EventPattern scarta i valori di ritorno. Da notare che NestJS 12 ha deprecato la risposta dagli handler @EventPattern sulla maggior parte dei trasporti.

Come carica e collega NestJS i file .proto per gRPC?

NestJS usa il pacchetto @grpc/proto-loader per parsare i file .proto all'avvio e generare descrittori di servizio. La configurazione GrpcOptions specifica il percorso proto e il nome del pacchetto. I controller annotati con @GrpcMethod('ServiceName', 'MethodName') vengono matchati con i descrittori parsati. Se un metodo dichiarato nel proto non ha un handler corrispondente, NestJS lancia un errore al bootstrap.

Quando gRPC dovrebbe sostituire REST tra microservizi?

gRPC si adatta alle chiamate service-to-service interne dove type safety, performance e streaming sono importanti. Protocol Buffers producono payload piu piccoli e serializzazione piu veloce di JSON. Il multiplexing HTTP/2 riduce l'overhead delle connessioni. REST rimane preferibile per API pubbliche, client browser e integrazioni con terze parti dove leggibilita umana e ampio supporto degli strumenti superano le pure prestazioni.

Come funziona il pattern hybrid application?

NestFactory.create() avvia un server HTTP. Chiamare app.connectMicroservice() collega trasporti aggiuntivi (gRPC, Kafka, Redis o qualsiasi transporter supportato) alla stessa istanza applicazione NestJS. Tutti i trasporti condividono lo stesso module tree, container DI e pipeline middleware. app.startAllMicroservices() avvia tutti i transporter connessi, e app.listen() avvia il layer HTTP.

Quali pattern di affidabilita prevengono i fallimenti a cascata?

Tre pattern sono i piu importanti: timeout (ogni chiamata RPC necessita una deadline), retry con jitter (solo per operazioni idempotenti, con backoff esponenziale piu jitter casuale per evitare thundering herd), e circuit breaker (aperto dopo N fallimenti, semi-aperto dopo un cooldown, chiuso quando gli health check passano). L'integrazione RxJS di NestJS rende timeout e retry componibili attraverso operatori pipe.

Cosa e cambiato per i microservizi in NestJS 12?

NestJS 12 ha aggiunto filtri exception gRPC per gestione errori specifici per status, pattern regexp Kafka per handler multi-topic, supporto NATS v3 con un nuovo pacchetto client, pre-request hooks per handler microservizi, e gestione backpressure stream gRPC migliorata. L'avviso di deprecazione @EventPattern segnala un futuro cambio comportamentale dove gli event handler non risponderanno piu alle chiamate send().

Per pratica approfondita sui pattern architetturali NestJS, il modulo NestJS modules and dependency injection copre i fondamentali DI alla base della composizione microservizi. Il modulo middleware and interceptors affronta gli aspetti trasversali condivisi tra trasporti. La guida NestJS e GraphQL copre un'alternativa a gRPC per API di servizio tipizzate.

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Fonti

Punti Chiave per i Microservizi NestJS nel 2026

  • L'astrazione del trasporto di NestJS 12 permette ai servizi di passare tra TCP, gRPC, Kafka e NATS senza riscrivere la logica handler: i decorator @MessagePattern e @EventPattern funzionano in modo identico su tutti i trasporti
  • gRPC con Protocol Buffers impone contratti rigorosi tra servizi attraverso file .proto, intercettando errori di integrazione a compile time invece che in produzione
  • Le applicazioni ibride combinano HTTP e gRPC sulla stessa istanza NestJS, eliminando la necessita di fare deploy di servizi separati per interfacce pubbliche e interne
  • Il server streaming via Observable RxJS abilita feed di dati in tempo reale senza complessita WebSocket, con NestJS 12 che aggiunge gestione backpressure per consumer lenti
  • Ogni chiamata RPC interna richiede una deadline, i retry si applicano solo a operazioni idempotenti, e i circuit breaker prevengono che un singolo servizio lento causi un'interruzione totale a cascata
  • I confini dei servizi seguono il domain-driven design: un bounded context per servizio, eventi asincroni per comunicazione cross-domain, pacchetti proto condivisi per l'applicazione dei contratti

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Sfida del giorno

Sapresti trovare il bug in Node.js / NestJS?

Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Anthony Fillion-Maillet

Scritto da

Anthony Fillion-Maillet

Fondatore di SharpSkill

Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.

Aggiornato il 22 ottobre 2026

Tag

#nestjs
#microservices
#grpc
#nodejs
#typescript
#architecture

Condividi

Articoli correlati