typescript
45 lines · 8 steps
Manual RabbitMQ acks in a NestJS controller
A NestJS microservice controller that handles RPC messages over RabbitMQ and acknowledges them by hand.
Explained by
highlit
1import { Controller } from '@nestjs/common';
2import {
3 MessagePattern,
4 Payload,
5 Ctx,
6 RmqContext,
7 RpcException,
8} from '@nestjs/microservices';
9import { OrdersService } from './orders.service';
10import { CreateOrderDto } from './dto/create-order.dto';
11
12@Controller()
13export class OrdersController {
14 constructor(private readonly ordersService: OrdersService) {}
15
16 @MessagePattern({ cmd: 'create_order' })
17 async createOrder(
18 @Payload() dto: CreateOrderDto,
19 @Ctx() context: RmqContext,
20 ) {
21 const channel = context.getChannelRef();
22 const originalMsg = context.getMessage();
23
24 try {
25 const order = await this.ordersService.create(dto);
26 channel.ack(originalMsg);
27 return { id: order.id, status: order.status, total: order.total };
28 } catch (err) {
29 channel.nack(originalMsg, false, false);
30 throw new RpcException({
31 code: 'ORDER_CREATION_FAILED',
32 message: err instanceof Error ? err.message : 'Unknown error',
33 });
34 }
35 }
36
37 @MessagePattern({ cmd: 'get_order' })
38 async getOrder(@Payload('id') id: string) {
39 const order = await this.ordersService.findById(id);
40 if (!order) {
41 throw new RpcException({ code: 'NOT_FOUND', message: `Order ${id} not found` });
42 }
43 return order;
44 }
45}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Manual acknowledgement lets you commit or reject a message only after your business logic succeeds or fails.
- 2Nacking with requeue set to false routes poison messages to a dead-letter queue instead of looping forever.
- 3RpcException carries structured error data back to the caller across the microservice transport.
Related explainers
rust
use std::convert::TryFrom; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HttpStatus {
Converting HTTP codes with TryFrom in Rust
enums
error-handling
trait-implementation
Intermediate
8 steps
typescript
type LazyImageOptions = { rootMargin?: string; loadedClass?: string; };
Lazy-loading images with IntersectionObserver
intersectionobserver
lazy-loading
performance
Intermediate
7 steps
typescript
type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
Type-safe deep merge in TypeScript
recursion
conditional-types
mapped-types
Advanced
7 steps
typescript
type CurrencyFormatOptions = { locale?: string; currency: string; showDecimals?: boolean;
Caching Intl.NumberFormat for currency
memoization
internationalization
caching
Intermediate
9 steps
rust
use chrono::NaiveDate; use serde::{Deserialize, Deserializer}; #[derive(Debug, Deserialize)]
Custom date parsing with serde in Rust
serde
deserialization
csv-parsing
Intermediate
7 steps
python
import uuid from pathlib import Path from fastapi import APIRouter, File, Form, HTTPException, UploadFile
Handling multipart file uploads in FastAPI
file-upload
validation
multipart-form
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/manual-rabbitmq-acks-in-a-nestjs-controller-explained-typescript-dba8/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.