What does
@nestjs/microservicesprovide?It lets a Nest app receive and send messages over a transport instead of HTTP, using the same modules, DI, pipes, guards and filters. Built-in transports: TCP, Redis, NATS, MQTT, RabbitMQ, Kafka and gRPC, plus custom ones.
// a pure microservice const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, { transport: Transport.RMQ, options: { urls: [process.env.RABBIT_URL], queue: 'orders' }, }); await app.listen(); // or an HTTP app that also consumes messages (hybrid) app.connectMicroservice<MicroserviceOptions>({ transport: Transport.KAFKA, options: kafkaOptions }); await app.startAllMicroservices();Handlers are controller methods decorated with
@MessagePattern()or@EventPattern()instead of@Get().What is the difference between
@MessagePatternand@EventPattern?@MessagePatternis request and response: the caller waits for the handler's return value.@EventPatternis fire and forget: the publisher gets no reply, and several services can react to one event.@Controller() export class InventoryController { @MessagePattern({ cmd: 'stock.check' }) check(@Payload() sku: string) { return this.stock.available(sku); // sent back to the caller } @EventPattern('order.placed') async reserve(@Payload() order: OrderPlaced) { await this.stock.reserve(order.items); // no reply } }Use messages when the caller needs the answer to continue; use events to announce that something happened. Events decouple services, but the publisher cannot know whether anyone handled them.
Why does
client.send()do nothing until you subscribe?ClientProxy.send()returns a cold RxJS Observable: the message is published only when something subscribes. Returning it from a controller works because Nest subscribes; calling it in a service and ignoring the result sends nothing.@Injectable() export class CheckoutService { constructor(@Inject('INVENTORY') private inventory: ClientProxy) {} async canBuy(sku: string) { return firstValueFrom(this.inventory.send({ cmd: 'stock.check' }, sku)); } orderPlaced(order: Order) { this.inventory.emit('order.placed', order); // sent right away } }emit()publishes immediately even without a subscriber. Addtimeout()tosend()calls so a missing consumer does not hang a request.What is the difference between code first and schema first GraphQL in NestJS?
With
@nestjs/graphql, code first means you write TypeScript classes with decorators and Nest generates the schema; schema first means you write the.graphqlSDL and Nest generates TypeScript types from it.// code first @ObjectType() export class Book { @Field(() => ID) id: string; @Field() title: string; } @Resolver(() => Book) export class BooksResolver { constructor(private books: BooksService) {} @Query(() => [Book]) books() { return this.books.findAll(); } }Code first keeps one source of truth in TypeScript and suits teams that live in the backend. Schema first suits teams that design the API contract first or share it with other languages. Resolvers, guards and pipes work the same in both.
How do you handle WebSockets in NestJS?
With a gateway: a class decorated with
@WebSocketGateway()from@nestjs/websockets, using Socket.IO by default (@nestjs/platform-socket.io) or plainws.@WebSocketGateway({ namespace: 'chat', cors: { origin: 'https://app.example.com' } }) export class ChatGateway implements OnGatewayConnection { @WebSocketServer() server: Server; handleConnection(client: Socket) { client.join(String(client.handshake.query.room)); } @SubscribeMessage('message') onMessage(@MessageBody() body: { room: string; text: string }) { this.server.to(body.room).emit('message', body.text); } }Guards, pipes and filters work on gateways (throw
WsException). To scale to several instances, add the Socket.IO Redis adapter so events reach clients connected to other servers.How do you run background jobs in NestJS?
Use a queue backed by Redis with
@nestjs/bullmq: the API adds a job and answers at once, and a worker processes it with retries.@Module({ imports: [BullModule.registerQueue({ name: 'emails' })] }) export class EmailsModule {} @Injectable() export class SignupService { constructor(@InjectQueue('emails') private emails: Queue) {} async signup(user: User) { await this.emails.add('welcome', { userId: user.id }, { attempts: 3 }); } } @Processor('emails') export class EmailsProcessor extends WorkerHost { async process(job: Job<{ userId: string }>) { // send the email; a throw makes BullMQ retry } }Use it for anything slow or failure-prone that the user should not wait for: emails, image processing, webhooks to third parties. Jobs should be idempotent, since a retry may run one twice.
How do you schedule a cron job in NestJS, and what goes wrong with several instances?
@nestjs/scheduleruns methods on a schedule inside the app:@Injectable() export class CleanupService { @Cron(CronExpression.EVERY_DAY_AT_3AM, { timeZone: 'UTC' }) async purgeExpiredSessions() { await this.sessions.deleteExpired(); } }Register
ScheduleModule.forRoot()once. The trap: with three replicas, the job runs three times. Fixes: run scheduled work in a single dedicated worker, take a distributed lock (a Redis key with an expiry, or a database advisory lock) before running, or move the schedule to the platform (a Kubernetes CronJob, a cloud scheduler) calling an endpoint or queuing a job.How do you stream server-sent events from NestJS?
Decorate a method with
@Sse()and return an Observable ofMessageEventobjects; Nest keeps the response open and writes each value as an event.@Controller('jobs') export class JobsController { @Sse(':id/progress') progress(@Param('id') id: string): Observable<MessageEvent> { return interval(1000).pipe( map((n) => ({ data: { id, percent: Math.min(n * 10, 100) } })), takeWhile((e) => e.data.percent < 100, true), ); } }SSE is one way (server to client) over plain HTTP, reconnects by itself in the browser, and passes through most proxies, which makes it simpler than WebSockets for progress bars and notifications. NestJS 12.x adds
@SseSignal(), anAbortSignalthat fires when the stream ends, for cleaning up resources.How do you version a REST API in NestJS?
Enable versioning in
main.tsand give controllers or routes a version:app.setGlobalPrefix('api'); app.enableVersioning({ type: VersioningType.URI }); @Controller({ path: 'cats', version: '1' }) export class CatsV1Controller { @Get() all() { return ['Tom']; } } @Controller({ path: 'cats', version: '2' }) export class CatsV2Controller { @Get() all() { return [{ name: 'Tom', age: 3 }]; } }This serves
/api/v1/catsand/api/v2/cats;/api/catsis404unless you setdefaultVersion. Other types:HEADER(a custom header),MEDIA_TYPE(Accept: application/json;v=2) andCUSTOM.@Version('2')on a method overrides the controller's version for one route.How do you cache responses in NestJS?
@nestjs/cache-managerprovidesCacheModuleandCacheInterceptor, which cachesGETresponses by URL:@Module({ imports: [CacheModule.register({ ttl: 30_000 })] }) // ms export class CatalogModule {} @Controller('catalog') @UseInterceptors(CacheInterceptor) export class CatalogController { @Get('popular') @CacheTTL(60_000) popular() { return this.catalog.popular(); } }For per-user data, the default URL key is wrong (two users share one entry): override
trackBy()or cache in the service withcacheManager.get/setand a key that includes the user. Use a shared store such as Redis when you run several instances, and plan how a write invalidates the entries it affects.
NestJS Microservices and GraphQL Interview Questions
Message transports, GraphQL, WebSockets, queues, cron jobs, server-sent events, versioning and caching.