From harness-claude
Connects NestJS services using ClientsModule, @MessagePattern/@EventPattern with TCP/Redis transports for request/response messaging and event broadcasting in microservices.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Connect services with ClientsModule, @MessagePattern, @EventPattern, and TCP/Redis transport
Guides NestJS development with official docs on controllers, providers, modules, middleware, guards, pipes, interceptors, DI, GraphQL, WebSockets, microservices, Swagger, security, and testing.
Assists NestJS development for Node.js server-side apps, covering controllers, modules, providers, dependency injection, pipes, guards, interceptors, and REST/GraphQL APIs.
Provides NestJS patterns for scalable Node.js/TypeScript backends: modular architecture, dependency injection, DTO validation, repositories, and events.
Share bugs, ideas, or general feedback.
Connect services with ClientsModule, @MessagePattern, @EventPattern, and TCP/Redis transport
// main.ts
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
});
await app.listen();
@Controller()
export class OrdersController {
@MessagePattern({ cmd: 'get_order' }) // request/response
getOrder(@Payload() data: { id: string }): Promise<Order> {
return this.ordersService.findOne(data.id);
}
@EventPattern('order_created') // fire-and-forget
async handleOrderCreated(@Payload() data: OrderCreatedEvent): Promise<void> {
await this.inventoryService.reserve(data.items);
}
}
// register the client in a module
@Module({
imports: [
ClientsModule.register([
{
name: 'ORDERS_SERVICE',
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
},
]),
],
})
// inject and use
@Injectable()
export class ApiGatewayService {
constructor(@InjectClient('ORDERS_SERVICE') private client: ClientProxy) {}
getOrder(id: string): Observable<Order> {
return this.client.send<Order>({ cmd: 'get_order' }, { id });
}
notifyOrderCreated(event: OrderCreatedEvent): Observable<void> {
return this.client.emit('order_created', event);
}
}
const app = await NestFactory.create(AppModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
});
await app.startAllMicroservices();
await app.listen(3000);
RpcException inside message handlers — it serializes cleanly to the caller.Transport comparison:
send() vs emit(): send() is request/response — it returns an Observable that emits the reply. emit() is fire-and-forget — it returns an Observable that completes when the message is sent (no reply). Always subscribe() to both, or convert to Promise with firstValueFrom().
@Payload() and @Ctx(): Use @Payload() to extract the message payload. Use @Ctx() to get the transport context (e.g., RmqContext for RabbitMQ to manually acknowledge messages).
Manual acknowledgment (RabbitMQ):
@MessagePattern('order_created')
async handle(@Payload() data: OrderCreatedEvent, @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const message = context.getMessage();
await this.processOrder(data);
channel.ack(message); // only ack after successful processing
}
Serialization: By default, NestJS serializes/deserializes with JSON.stringify/parse. For binary protocols (protobuf, msgpack), configure a custom serializer/deserializer pair.
https://docs.nestjs.com/microservices/basics