NestJS has an opinionated architecture — modules, controllers, services, guards, interceptors. That structure is actually an advantage when working with Claude Code: the more conventions your project enforces, the more reliably an AI agent can navigate it. But only if you tell it the rules.
A generic TypeScript CLAUDE.md will generate working code. A NestJS-specific CLAUDE.md will generate code that fits your module boundaries, uses the right decorators, respects the DI container, and passes your tests on the first try. The difference is significant in practice.
We analyzed real NestJS projects using Claude Code and assembled the patterns that actually reduce back-and-forth. This guide covers the complete setup: CLAUDE.md template, AGENTS.md for multi-agent workflows, common failure modes, and testing patterns specific to NestJS.
Browse our collection of real-world CLAUDE.md and AGENTS.md examples if you want to see how other teams configure their AI assistants.
Why NestJS Needs Its Own CLAUDE.md
Without framework-specific instructions, Claude Code treats NestJS like a generic TypeScript project. The results are predictable:
- Services import from controllers (inverted dependency graph)
process.env.DATABASE_URLappears directly in service files instead of going throughConfigService- DTOs are written as plain interfaces instead of classes with
class-validatordecorators - Modules export services that should stay internal
- Property injection replaces constructor injection in places where it breaks the DI scope
- Test files mock the entire module instead of using
TestingModuleproperly
None of this is wrong in principle. It’s wrong for NestJS specifically, and fixing it after the fact costs more time than writing the rules upfront.
The CLAUDE.md Template
Copy this into your project’s CLAUDE.md. The sections are ordered by frequency of mistakes — the first few rules will prevent the most friction.
# CLAUDE.md — NestJS Project
## Architecture Rules
- **Module boundaries**: Never import a service from another module unless it is explicitly exported by that module's `@Module()` decorator. If you need it, add it to `exports: []`.
- **Dependency direction**: Services depend on repositories/external services. Controllers depend on services. Services NEVER import from controllers.
- **Constructor injection only**: Use constructor injection for all dependencies. Property injection (`@Inject()` on a class field) is forbidden except for optional dependencies with `@Optional()`.
- **ConfigService**: Never access `process.env` directly in services or controllers. Always inject `ConfigService` and call `this.configService.get<string>('KEY')`.
- **Providers are singletons by default**: Design services accordingly. If you need request-scoped providers, set `scope: Scope.REQUEST` explicitly and document why.
## File and Naming Conventions
- Module files: `{feature}.module.ts`
- Controller files: `{feature}.controller.ts` — one resource per controller
- Service files: `{feature}.service.ts`
- Repository files: `{feature}.repository.ts` (if using custom repositories)
- DTO files: `create-{feature}.dto.ts`, `update-{feature}.dto.ts`
- Guard files: `{name}.guard.ts` — class name `{Name}Guard`
- Interceptor files: `{name}.interceptor.ts` — class name `{Name}Interceptor`
- Pipe files: `{name}.pipe.ts` — class name `{Name}Pipe`
- Exception filter files: `{name}.filter.ts` — class name `{Name}Filter`
## DTOs and Validation
- DTOs must be classes, not interfaces. `class-validator` decorators only work on class instances.
- Every DTO property that requires validation must have at least one `class-validator` decorator.
- Use `@IsOptional()` for optional fields — never use `?` alone without it.
- Use `@Type(() => SubDto)` from `class-transformer` for nested object transformation.
- Enable `whitelist: true` and `forbidNonWhitelisted: true` in the global `ValidationPipe`.
```typescript
// Correct DTO pattern
import { IsString, IsEmail, IsOptional, MinLength } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
@IsOptional()
@IsString()
bio?: string;
}
ORM Integration
If using TypeORM
- Entity files live in
src/{feature}/entities/{feature}.entity.ts - Use the
@InjectRepository(Entity)decorator in service constructors - Never call
dataSource.query()for business logic — use the repository pattern - Migrations go in
src/migrations/— never usesynchronize: truein production
If using Prisma
- The
PrismaServiceis a shared module provider — importPrismaModuleinstead of creating new instances - Use
prisma.$transaction()for operations that must succeed or fail together - Raw SQL with
prisma.$queryRawis permitted only for complex analytics queries
Testing
- Unit tests: use
Test.createTestingModule()from@nestjs/testing. Never mock the entire module; mock only the direct dependencies. - Integration tests: use
supertestwith the fullNestApplicationinstance. - Test files:
{name}.spec.tsfor unit tests,{name}.e2e-spec.tsfor e2e tests. - Mock pattern for services:
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: {
findOne: jest.fn(),
save: jest.fn(),
find: jest.fn(),
},
},
],
}).compile();
NestJS CLI Commands
When generating code, prefer the NestJS CLI patterns:
nest g module {feature}— generates module file and updates AppModulenest g controller {feature} --no-spec— if spec is handled separatelynest g service {feature}— generates service with spec file- Always generate in the correct feature directory:
nest g service users/user
Common Mistakes to Avoid
- Circular dependencies: if Module A imports Module B and Module B imports Module A, use
forwardRef(() => ModuleB)— but first ask whether the architecture is correct. - Global modules: use
@Global()sparingly. Only for truly app-wide providers likePrismaServiceorConfigService. - Async module initialization: use
forRootAsync()pattern when the module config depends on another provider.
## Setting Up Multi-Agent Workflows with AGENTS.md
For larger NestJS projects — multiple feature domains, parallel development — you can assign Claude Code subagents to specific modules. The AGENTS.md file scopes agent instructions to directory paths.
```markdown
# AGENTS.md
## Global Rules
Apply to all agents working in this codebase:
- Follow all rules in CLAUDE.md
- Run `npm run lint` and `npm run test` before marking any task as complete
- Never modify `.env` files; use `ConfigService` for environment access
## /src/users/
Agent scope: User management domain.
- This module owns the `User` entity. No other module should modify it.
- Authentication is handled by `/src/auth/` — do not duplicate auth logic here.
- The `UserService` is exported and consumed by `AuthModule`. Don't change the exported interface without coordinating with auth changes.
## /src/auth/
Agent scope: Authentication and authorization.
- JWT strategy lives here. Guards that protect routes import from this module.
- Never store plaintext passwords. Always use bcrypt with a cost factor of 12.
- The `AuthModule` imports `UserModule` for user lookup — this is intentional and not a circular dependency.
## /src/shared/
Agent scope: Shared utilities, decorators, interceptors, pipes.
- Code here must have zero feature-specific imports. It should work in any NestJS project.
- If you find yourself importing from a feature module here, it belongs in that feature module instead.
## /test/
Agent scope: End-to-end tests only.
- Use the full application with `NestFactory.create(AppModule)` and `supertest`.
- Test data cleanup is handled by `afterEach()` — always restore state.
The Module Structure Claude Code Works Best With
Before you write your CLAUDE.md, it helps to establish a consistent module structure. Claude Code navigates predictable layouts significantly better than ad-hoc organization.
We recommend the following structure for each feature domain:
src/
users/
dto/
create-user.dto.ts
update-user.dto.ts
entities/
user.entity.ts
users.controller.ts
users.controller.spec.ts
users.service.ts
users.service.spec.ts
users.module.ts
auth/
guards/
jwt.guard.ts
roles.guard.ts
strategies/
jwt.strategy.ts
dto/
login.dto.ts
auth.controller.ts
auth.service.ts
auth.module.ts
shared/
interceptors/
logging.interceptor.ts
pipes/
parse-object-id.pipe.ts
decorators/
current-user.decorator.ts
With this layout, your CLAUDE.md can reference specific paths. When you tell Claude Code “add rate limiting to the auth controller,” it knows exactly where to look and what to modify.
Testing Patterns That Work Well with Claude Code
NestJS testing has some quirks that trip up AI agents without explicit guidance. Two patterns in particular improve reliability significantly.
Pattern 1: Typed Mock Factories
Instead of relying on Claude Code to infer mock structure from the source, define a typed mock factory at the top of each spec file:
// users.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { UserService } from './users.service';
import { User } from './entities/user.entity';
const mockUserRepository = () => ({
findOne: jest.fn(),
find: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
create: jest.fn(),
});
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
describe('UserService', () => {
let service: UserService;
let repository: MockRepository<User>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useFactory: mockUserRepository,
},
],
}).compile();
service = module.get<UserService>(UserService);
repository = module.get(getRepositoryToken(User));
});
When Claude Code sees this pattern established in one spec file, it replicates it correctly in new spec files.
Pattern 2: E2E Test Database Reset
For e2e tests, define a reset utility that Claude Code can call in teardown:
// test/helpers/reset-db.ts
import { DataSource } from 'typeorm';
export async function resetDatabase(dataSource: DataSource): Promise<void> {
const entities = dataSource.entityMetadatas;
for (const entity of entities) {
const repository = dataSource.getRepository(entity.name);
await repository.clear();
}
}
Add to your CLAUDE.md:
## E2E Test Teardown
After each e2e test that writes to the database, call `resetDatabase(dataSource)` from `test/helpers/reset-db.ts`. Never use `TRUNCATE CASCADE` directly in test files.
Guard and Interceptor Patterns
Guards and interceptors are where Claude Code makes the most mistakes without explicit rules. The pattern below, added to your CLAUDE.md’s “Common Mistakes” section, prevents most issues:
## Guards
- Guards must implement `CanActivate` and return `Observable<boolean> | Promise<boolean> | boolean`.
- JWT verification belongs in `AuthGuard('jwt')` from `@nestjs/passport` — don't write custom JWT decoding in guards.
- Role checks use a separate `RolesGuard` that reads the `@Roles()` metadata decorator.
- Apply guards with `@UseGuards(JwtAuthGuard, RolesGuard)` — order matters. Auth before roles.
## Interceptors
- Interceptors for logging and response transformation go in `shared/interceptors/`.
- Use `tap()` for side effects (logging), `map()` for response transformation.
- Never throw exceptions in interceptors — that's what exception filters are for.
Prompting Patterns That Work Well for NestJS
Beyond the static rules in CLAUDE.md, certain prompting patterns produce better results for NestJS-specific work:
For generating a new feature module:
“Create a
productsfeature module following our established pattern. It needs a Product entity withid,name,price(decimal),stock(integer), andcreatedAt. The service should exposefindAll(),findOne(id),create(dto),update(id, dto), andremove(id). Wire it into AppModule.”
For adding a guard to an existing route:
“Add JWT authentication to all routes in ProductsController except
GET /productsandGET /products/:id(those stay public). Use the existingJwtAuthGuardfromsrc/auth/guards/. Don’t modify the guard itself.”
For writing tests:
“Write unit tests for
ProductsService.create(). The method callsrepository.create(),repository.save(), and should throw aConflictExceptionif a product with the samenamealready exists. Use the mock repository pattern fromusers.service.spec.ts.”
Specific references to existing patterns and files matter. Claude Code navigates concrete references better than abstract descriptions.
Integration with the Prompt Shelf Gallery
Real NestJS CLAUDE.md examples from public repos are available in our gallery. The most useful patterns we’ve seen in production:
- Microservices setups where each service has its own CLAUDE.md scoped to that service’s domain
- Event-driven architectures with explicit AGENTS.md rules about which agent handles event publishers vs subscribers
- Hybrid REST+GraphQL projects with separate rules for resolver vs controller code
If you’re building a new NestJS project, start with this template and adjust as your architecture firms up. The rules that matter most are the ones that reflect actual decisions your team has already made — DI patterns, ORM choice, testing approach. Write those down first and let Claude Code learn the rest from context.