The Nest you already write
Controllers, @Payload(), @Ctx(), guards, pipes, interceptors and filters work the way Nest documents them for every transport. What changes is what runs the handler: an Inngest function, invoked over HTTP, with durable steps underneath.
orders.handler.ts
@Controller()
@UseFilters(InngestExceptionFilter)
export class OrdersHandler {
@InngestEventPattern('orders.placed', { concurrency: { limit: 5 } })
async place(@Payload() order: Order, @Ctx() context: InngestContext) {
const step = context.getStep();
await step.run('reserve-stock', () => this.stock.reserve(order));
await step.sleep('cool-off', '1h');
await step.run('confirm', () => this.mailer.confirm(order));
}
}
main.ts
const app = await NestFactory.create(AppModule);
app.connectMicroservice({
strategy: new InngestStrategy({ inngest, httpAdapter: app.getHttpAdapter() }),
});
await app.startAllMicroservices();
await app.listen(3000);
// anywhere else
await lastValueFrom(events.emit('orders.placed', order));
Start here, or read why Nest's default exception filter needs replacing before going to production.