Claude Code NestJS CLAUDE.md TypeScript Backend Node.js

Claude Code for NestJS: CLAUDE.md Template, DI Patterns, and Testing Workflow (2026)

The Prompt Shelf ·

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_URL appears directly in service files instead of going through ConfigService
  • DTOs are written as plain interfaces instead of classes with class-validator decorators
  • 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 TestingModule properly

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 use synchronize: true in production

If using Prisma

  • The PrismaService is a shared module provider — import PrismaModule instead of creating new instances
  • Use prisma.$transaction() for operations that must succeed or fail together
  • Raw SQL with prisma.$queryRaw is 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 supertest with the full NestApplication instance.
  • Test files: {name}.spec.ts for unit tests, {name}.e2e-spec.ts for 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 AppModule
  • nest g controller {feature} --no-spec — if spec is handled separately
  • nest g service {feature} — generates service with spec file
  • Always generate in the correct feature directory: nest g service users/user

Common Mistakes to Avoid

  1. 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.
  2. Global modules: use @Global() sparingly. Only for truly app-wide providers like PrismaService or ConfigService.
  3. 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 products feature module following our established pattern. It needs a Product entity with id, name, price (decimal), stock (integer), and createdAt. The service should expose findAll(), findOne(id), create(dto), update(id, dto), and remove(id). Wire it into AppModule.”

For adding a guard to an existing route:

“Add JWT authentication to all routes in ProductsController except GET /products and GET /products/:id (those stay public). Use the existing JwtAuthGuard from src/auth/guards/. Don’t modify the guard itself.”

For writing tests:

“Write unit tests for ProductsService.create(). The method calls repository.create(), repository.save(), and should throw a ConflictException if a product with the same name already exists. Use the mock repository pattern from users.service.spec.ts.”

Specific references to existing patterns and files matter. Claude Code navigates concrete references better than abstract descriptions.

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.

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules