NestJS
Angular-style module system and DI for structured TypeScript backends.
NestJS applies Angular's module and DI model to Node.js backends. Each feature gets its own module with explicit provider declarations; the injector resolves the dependency graph at bootstrap, which means service dependencies are visible in constructors rather than scattered across imports. Decorators handle the cross-cutting concerns — route binding, validation pipes, guard execution order — at the framework level so application code stays focused on domain logic.

My module topology follows a three-layer split: a CoreModule registered globally for singleton infrastructure (config, database connections, logging), SharedModule for reusable pipes, guards, and DTOs that multiple features import, and individual feature modules that are self-contained — their services, controllers, and repositories live together and the module exports only what other features genuinely need. Constructor injection is the only pattern I use; I avoid property injection because it hides dependencies from the type signature and makes unit tests harder to set up without the full DI container. For custom providers I lean on factory providers when the instantiation depends on async config (database URLs, secrets), and useExisting aliases when I need to swap an implementation behind an interface token for testing. Guards handle authentication and authorization as a pipeline — JwtAuthGuard runs first to populate the request user, then role or permission guards check the decorated metadata. Interceptors cover response shape normalization and request timing; I keep them narrow and stateless so they can be applied globally without side effects. For Express-to-NestJS migrations I use NestJS's Express adapter and mount the existing Express app as middleware during the transition — new routes go through NestJS controllers while legacy routes still hit the original handlers, which lets me migrate endpoint by endpoint without a cutover.
Application Architecture
I structure applications around a CoreModule (global singletons: config, DB, logger), a SharedModule (reusable pipes, guards, DTOs exported to all features), and individual feature modules that are fully self-contained. Feature modules export only the surface other modules need, which keeps the dependency graph explicit and prevents circular imports from creeping in.
Express to NestJS Migration
I mount the existing Express app as middleware inside NestJS's Express adapter — the legacy router keeps handling its routes while I build new controllers in NestJS alongside it. Each migrated route gets deleted from the legacy router once its NestJS counterpart is verified in production. No cutover window, no flag day.
Microservices Setup
Transport selection depends on the delivery guarantee and consumer topology: Redis for simple fan-out with fire-and-forget semantics, RabbitMQ when I need per-message ack, dead-letter exchange, and durable queues, Kafka when event ordering across partitions or replay from offset matters. In all cases I configure TerminusModule health indicators, set explicit retry counts in the client options, and wire up a dead-letter queue or topic before the first deployment.
REST API Backends
CRUD-heavy REST services where I use class-validator DTOs on every incoming body, JwtAuthGuard + RolesGuard on protected routes, a global ThrottlerGuard for rate limiting, and @ApiTags / @ApiOperation decorators so the Swagger doc stays in sync with the implementation without a separate spec file.
GraphQL APIs
Code-first GraphQL schemas where resolvers are plain NestJS providers with constructor-injected services. I wire DataLoader instances through a per-request context object to batch and cache database calls within a single query execution, which eliminates N+1 without pulling in a separate batching library.
Job Processing
BullMQ-backed job queues where each job type gets its own processor class decorated with @Processor and @Process. I set explicit attempt counts, exponential backoff, and a dead-letter queue on every queue definition so failed jobs are observable and replayable rather than silently dropped.
Let's talk NestJS.
No pitch. Just a technical conversation about the problem you're working on.