From harness-claude
Organizes NestJS applications using feature modules, controlled exports, dynamic configurations with forRoot/forFeature. Useful for structuring new features, sharing providers without circular dependencies, and building reusable modules.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Organize NestJS applications with cohesive feature modules, controlled exports, and composable dynamic configurations
Provides NestJS patterns for scalable Node.js/TypeScript backends: modular architecture, dependency injection, DTO validation, repositories, and events.
Guides NestJS dependency injection using class, value, factory providers, modules, decorators, scopes, and best practices for modular Node.js apps.
Enforces NestJS best practices for modular architecture, dependency injection scoping, exception filters, class-validator DTO validation, and Drizzle ORM integration. Use when designing modules, providers, filters, DTOs, or ORM in NestJS apps.
Share bugs, ideas, or general feedback.
Organize NestJS applications with cohesive feature modules, controlled exports, and composable dynamic configurations
UsersModule, OrdersModule, AuthModule). Group the controller, service, and repository for that domain inside it.providers array of the module that owns it. Only add it to exports when another module needs to inject it.@Global() sparingly: only for truly cross-cutting utilities (logging, config, event bus). Overuse destroys encapsulation.forRoot() / forFeature() static methods for modules that need external configuration:@Module({})
export class DatabaseModule {
static forRoot(options: DatabaseOptions): DynamicModule {
return {
module: DatabaseModule,
providers: [{ provide: DATABASE_OPTIONS, useValue: options }, DatabaseService],
exports: [DatabaseService],
global: true,
};
}
}
forFeature() for per-feature registration (e.g., registering Prisma models or TypeORM repositories for a specific module).AppModule thin — it should only import feature modules and global infrastructure modules, not declare providers directly.SharedModule.NestJS modules are the primary unit of application organization. They enforce explicit dependency declaration: a module cannot use a provider unless it either declares it internally or imports a module that exports it. This makes dependency graphs auditable and prevents accidental coupling.
Module anatomy:
@Module({
imports: [TypeOrmModule.forFeature([User])], // modules whose exports you need
controllers: [UsersController], // route handlers scoped to this module
providers: [UsersService, UsersRepository], // services, repos, guards, etc.
exports: [UsersService], // what other modules may inject
})
export class UsersModule {}
Dynamic modules solve the configuration problem. A static module cannot accept runtime configuration; forRoot() returns a DynamicModule object that NestJS treats identically to a static module but allows passing options:
// In AppModule:
DatabaseModule.forRoot({ url: process.env.DATABASE_URL });
Global modules (@Global()) register their exports into every module without requiring an explicit imports declaration. Use this for config, logging, and event buses — not for business-logic services where explicit imports communicate intent.
Shared modules are a common pattern for breaking circular dependencies. Extract the shared provider into its own module, export it, and import SharedModule from both dependents.
Trade-offs:
https://docs.nestjs.com/modules