install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/nestjs-module-pattern" ~/.claude/skills/intense-visions-harness-engineering-nestjs-module-pattern-6d8433 && rm -rf "$T"
manifest:
agents/skills/codex/nestjs-module-pattern/SKILL.mdsource content
NestJS Module Pattern
Organize NestJS applications with cohesive feature modules, controlled exports, and composable dynamic configurations
When to Use
- Starting a new NestJS feature and deciding where to place providers, controllers, and services
- Sharing providers between modules without creating circular dependencies
- Building reusable library modules that accept configuration (e.g., a database module, auth module)
- Registering global utilities (guards, interceptors, pipes) that should apply application-wide
Instructions
- Create one module per feature domain (e.g.,
,UsersModule
,OrdersModule
). Group the controller, service, and repository for that domain inside it.AuthModule - Declare every provider in the
array of the module that owns it. Only add it toproviders
when another module needs to inject it.exports - Import a module to gain access to its exported providers — never directly import a provider from another module's file.
- Use
sparingly: only for truly cross-cutting utilities (logging, config, event bus). Overuse destroys encapsulation.@Global() - Implement
/forRoot()
static methods for modules that need external configuration:forFeature()
@Module({}) export class DatabaseModule { static forRoot(options: DatabaseOptions): DynamicModule { return { module: DatabaseModule, providers: [{ provide: DATABASE_OPTIONS, useValue: options }, DatabaseService], exports: [DatabaseService], global: true, }; } }
- Use
for per-feature registration (e.g., registering Prisma models or TypeORM repositories for a specific module).forFeature() - Keep
thin — it should only import feature modules and global infrastructure modules, not declare providers directly.AppModule - Avoid circular module dependencies. If two modules need each other's providers, extract the shared logic into a third
.SharedModule
Details
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:
- Explicit imports are verbose but make coupling visible at a glance
- Global modules reduce boilerplate but hide dependencies — use them only for infrastructure
- Dynamic modules enable reusability but add complexity; prefer static modules until reuse is needed
Source
https://docs.nestjs.com/modules
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.