debug:nestjs

📁 snakeo/claude-debug-and-refactor-skills-plugin 📅 Jan 19, 2026
20
总安装量
19
周安装量
#17939
全站排名
安装命令
npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill debug:nestjs

Agent 安装分布

opencode 15
claude-code 15
gemini-cli 14
cursor 14
github-copilot 13
codex 13

Skill 文档

NestJS Debugging Guide

This guide provides a systematic approach to debugging NestJS applications. Use the four-phase methodology below to efficiently identify and resolve issues.

Common Error Patterns

1. Dependency Resolution Errors

Error Message:

Nest can't resolve dependencies of the <ProviderName> (?). Please make sure that the argument <DependencyName> at index [0] is available in the <ModuleName> context.

Common Causes:

  • Provider not added to module’s providers array
  • Missing @Injectable() decorator on service class
  • Incorrect import/export between modules
  • Typo in injection token name
  • Missing forRoot()/forRootAsync() configuration

Solutions:

// Ensure provider is in the module
@Module({
  providers: [MyService], // Add missing provider here
  exports: [MyService],   // Export if used by other modules
})
export class MyModule {}

// Ensure @Injectable() decorator exists
@Injectable()
export class MyService {
  constructor(private readonly dependency: OtherService) {}
}

// For external modules, import the module not just the service
@Module({
  imports: [OtherModule], // Import the module
})
export class MyModule {}

2. Circular Dependency Errors

Error Message:

Nest cannot create the module instance. The module at index [X] of the imports array is undefined.

Common Causes:

  • Two modules importing each other
  • Two services depending on each other
  • File-level circular imports (constants, types)

Solutions:

// Use forwardRef() for circular module dependencies
@Module({
  imports: [forwardRef(() => OtherModule)],
})
export class MyModule {}

// Use forwardRef() for circular service dependencies
@Injectable()
export class ServiceA {
  constructor(
    @Inject(forwardRef(() => ServiceB))
    private serviceB: ServiceB,
  ) {}
}

// Alternative: Extract shared logic to a third module
@Module({
  providers: [SharedService],
  exports: [SharedService],
})
export class SharedModule {}

3. Guard/Interceptor Issues

Error Message:

Cannot read property 'canActivate' of undefined
Cannot read property 'intercept' of undefined

Common Causes:

  • Guard/Interceptor not properly registered
  • Missing @UseGuards() or @UseInterceptors() decorator
  • Incorrect scope (request-scoped vs singleton)
  • Dependencies not available in guard/interceptor context

Solutions:

// Register globally in main.ts
app.useGlobalGuards(new AuthGuard());
app.useGlobalInterceptors(new LoggingInterceptor());

// Or register via module for DI support
@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: LoggingInterceptor,
    },
  ],
})
export class AppModule {}

// Controller-level registration
@UseGuards(AuthGuard)
@UseInterceptors(LoggingInterceptor)
@Controller('users')
export class UsersController {}

4. Pipe Validation Errors

Error Message:

An instance of an invalid class-validator value was provided
Validation failed (expected type)

Common Causes:

  • Missing class-transformer or class-validator packages
  • DTO not properly decorated with validation decorators
  • ValidationPipe not configured correctly
  • Transform option not enabled

Solutions:

// Enable ValidationPipe globally with proper options
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,           // Strip non-whitelisted properties
    forbidNonWhitelisted: true, // Throw on extra properties
    transform: true,           // Auto-transform payloads to DTO instances
    transformOptions: {
      enableImplicitConversion: true,
    },
  }),
);

// Properly decorate DTOs
import { IsString, IsInt, Min, IsOptional } from 'class-validator';
import { Type } from 'class-transformer';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsInt()
  @Min(0)
  @Type(() => Number)
  age: number;

  @IsOptional()
  @IsString()
  email?: string;
}

5. Microservice Communication Errors

Error Message:

There is no matching message handler defined in the remote service
Connection refused / ECONNREFUSED

Common Causes:

  • Message pattern mismatch between client and server
  • Transport configuration mismatch
  • Service not connected or not running
  • Serialization/deserialization issues

Solutions:

// Ensure matching patterns
// Server side
@MessagePattern({ cmd: 'get_user' })
async getUser(data: { id: number }) {
  return this.usersService.findOne(data.id);
}

// Client side - pattern must match exactly
const user = await this.client.send({ cmd: 'get_user' }, { id: 1 }).toPromise();

// Check transport configuration matches
// Server
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
  AppModule,
  {
    transport: Transport.TCP,
    options: { host: '0.0.0.0', port: 3001 },
  },
);

// Client module
ClientsModule.register([
  {
    name: 'USER_SERVICE',
    transport: Transport.TCP,
    options: { host: 'localhost', port: 3001 },
  },
]);

6. WebSocket/Gateway Errors

Error Message:

Gateway is not defined
WebSocket connection failed

Common Causes:

  • Missing @WebSocketGateway() decorator
  • Gateway not added to module providers
  • CORS issues with WebSocket connections
  • Adapter not properly configured

Solutions:

// Properly configure gateway
@WebSocketGateway({
  cors: {
    origin: '*',
  },
  namespace: '/events',
})
export class EventsGateway implements OnGatewayConnection {
  @WebSocketServer()
  server: Server;

  handleConnection(client: Socket) {
    console.log('Client connected:', client.id);
  }

  @SubscribeMessage('message')
  handleMessage(client: Socket, payload: any): string {
    return 'Hello world!';
  }
}

// Add to module
@Module({
  providers: [EventsGateway],
})
export class EventsModule {}

// Configure adapter in main.ts if needed
import { IoAdapter } from '@nestjs/platform-socket.io';
app.useWebSocketAdapter(new IoAdapter(app));

Debugging Tools

1. NestJS Debug Mode

# Start with debug flag
nest start --debug --watch

# Or add to package.json
"start:debug": "nest start --debug --watch"

2. VS Code Debugger Configuration

Create .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug NestJS",
      "runtimeExecutable": "npm",
      "runtimeArgs": ["run", "start:debug"],
      "console": "integratedTerminal",
      "restart": true,
      "autoAttachChildProcesses": true,
      "sourceMaps": true,
      "envFile": "${workspaceFolder}/.env"
    },
    {
      "type": "node",
      "request": "attach",
      "name": "Attach to NestJS",
      "port": 9229,
      "restart": true,
      "sourceMaps": true
    }
  ]
}

3. Docker Debug Configuration

# docker-compose.debug.yml
version: '3.8'
services:
  api:
    command: npm run start:debug
    ports:
      - "3000:3000"
      - "9229:9229"  # Debug port
    environment:
      - NODE_OPTIONS=--inspect=0.0.0.0:9229

4. Built-in Logger Service

import { Logger, Injectable } from '@nestjs/common';

@Injectable()
export class MyService {
  private readonly logger = new Logger(MyService.name);

  async doSomething() {
    this.logger.log('Processing started');
    this.logger.debug('Debug info', { data: someData });
    this.logger.warn('Warning message');
    this.logger.error('Error occurred', error.stack);
    this.logger.verbose('Verbose details');
  }
}

// Configure logger level in main.ts
const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn', 'log', 'debug', 'verbose'],
});

5. @nestjs/testing Utilities

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';

describe('UsersController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    })
    .overrideProvider(UsersService)
    .useValue(mockUsersService)
    .compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/users (GET)', () => {
    return request(app.getHttpServer())
      .get('/users')
      .expect(200)
      .expect({ data: [] });
  });
});

The Four Phases (NestJS-specific)

Phase 1: Gather Context

Before debugging, collect all relevant information:

# Check NestJS and dependency versions
npm list @nestjs/core @nestjs/common @nestjs/platform-express

# Check for peer dependency issues
npm ls 2>&1 | grep -i "peer dep"

# Review recent changes
git diff HEAD~5 --name-only | grep -E '\.(ts|json)$'

# Check environment configuration
cat .env | grep -v -E '^#|^$'

Key Questions:

  • When did the error start occurring?
  • What changes were made recently?
  • Is this reproducible in all environments?
  • Are there any relevant logs or stack traces?

Phase 2: Isolate the Problem

Narrow down the issue systematically:

// 1. Check module structure
import { Logger } from '@nestjs/common';

@Module({
  imports: [
    // Comment out imports one by one to isolate
    ConfigModule.forRoot(),
    // DatabaseModule,  // Try disabling
    // UsersModule,     // Try disabling
  ],
})
export class AppModule {
  constructor() {
    Logger.log('AppModule initialized');
  }
}

// 2. Add lifecycle logging
@Injectable()
export class MyService implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(MyService.name);

  onModuleInit() {
    this.logger.log('MyService initialized');
  }

  onModuleDestroy() {
    this.logger.log('MyService destroyed');
  }
}

// 3. Test dependency injection manually
@Controller()
export class TestController {
  constructor(
    @Optional() private myService?: MyService,
  ) {
    console.log('MyService injected:', !!this.myService);
  }
}

Phase 3: Debug and Fix

Apply targeted debugging techniques:

// 1. For DI issues - check the module graph
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: ['verbose'],  // Enable all logging
  });

  // Log all registered routes
  const server = app.getHttpServer();
  const router = server._events.request._router;
  console.log('Registered routes:', router.stack
    .filter(r => r.route)
    .map(r => `${Object.keys(r.route.methods)} ${r.route.path}`));

  await app.listen(3000);
}

// 2. For async configuration issues
@Module({
  imports: [
    ConfigModule.forRootAsync({
      useFactory: async () => {
        console.log('ConfigModule factory called');
        const config = await loadConfig();
        console.log('Config loaded:', config);
        return config;
      },
    }),
  ],
})
export class AppModule {}

// 3. For guard/interceptor debugging
@Injectable()
export class DebugGuard implements CanActivate {
  private readonly logger = new Logger(DebugGuard.name);

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    this.logger.debug(`Guard checking: ${request.method} ${request.url}`);
    this.logger.debug(`Headers: ${JSON.stringify(request.headers)}`);
    this.logger.debug(`User: ${JSON.stringify(request.user)}`);
    return true;  // Allow through for debugging
  }
}

Phase 4: Verify and Document

Ensure the fix is complete and documented:

// 1. Write a test for the fixed issue
describe('UserService', () => {
  it('should resolve dependencies correctly', async () => {
    const module = await Test.createTestingModule({
      imports: [UsersModule],
    }).compile();

    const service = module.get<UsersService>(UsersService);
    expect(service).toBeDefined();
    expect(service.userRepository).toBeDefined();
  });
});

// 2. Add error handling to prevent recurrence
@Injectable()
export class RobustService {
  private readonly logger = new Logger(RobustService.name);

  async riskyOperation() {
    try {
      return await this.externalCall();
    } catch (error) {
      this.logger.error(`Operation failed: ${error.message}`, error.stack);
      throw new InternalServerErrorException('Operation failed');
    }
  }
}

// 3. Document in comments
/**
 * UserService handles user-related operations.
 *
 * IMPORTANT: This service depends on DatabaseModule being imported
 * before UsersModule in AppModule to avoid circular dependency issues.
 *
 * @see https://docs.nestjs.com/fundamentals/circular-dependency
 */
@Injectable()
export class UsersService {}

Quick Reference Commands

Debugging Commands

# Start in debug mode
npm run start:debug

# Start with increased heap memory
NODE_OPTIONS="--max-old-space-size=4096" npm run start:debug

# Run with verbose logging
LOG_LEVEL=verbose npm run start

# Debug specific module loading
DEBUG=nest:* npm run start

# Check TypeScript compilation
npx tsc --noEmit

# Validate module structure
npx nest info

Common Fixes

# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install

# Rebuild NestJS
npm run build
rm -rf dist && npm run build

# Clear cache
npm cache clean --force

# Check for duplicate packages
npm dedupe

# Update NestJS packages
npx npm-check-updates -u "@nestjs/*"
npm install

Inspection Commands

# List all modules and providers
npx nest info

# Check circular dependencies
npx madge --circular --extensions ts src/

# Analyze bundle size
npx source-map-explorer dist/main.js

# Check TypeScript config
npx tsc --showConfig

# Verify imports
npx ts-unused-exports tsconfig.json

Docker Debugging

# Access container shell
docker exec -it <container_id> sh

# View container logs
docker logs -f <container_id>

# Check container environment
docker exec <container_id> env

# Debug with docker compose
docker compose -f docker-compose.debug.yml up

# Attach to running container
docker attach <container_id>

Exception Filters for Better Error Handling

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  private readonly logger = new Logger(AllExceptionsFilter.name);

  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    const status =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    const message =
      exception instanceof HttpException
        ? exception.getResponse()
        : 'Internal server error';

    const errorResponse = {
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      method: request.method,
      message: typeof message === 'string' ? message : (message as any).message,
    };

    this.logger.error(
      `${request.method} ${request.url} ${status}`,
      exception instanceof Error ? exception.stack : String(exception),
    );

    response.status(status).json(errorResponse);
  }
}

// Register globally
app.useGlobalFilters(new AllExceptionsFilter());

Performance Debugging

// 1. Add timing interceptor
@Injectable()
export class TimingInterceptor implements NestInterceptor {
  private readonly logger = new Logger(TimingInterceptor.name);

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    const request = context.switchToHttp().getRequest();

    return next.handle().pipe(
      tap(() => {
        const elapsed = Date.now() - now;
        this.logger.log(`${request.method} ${request.url} - ${elapsed}ms`);

        if (elapsed > 1000) {
          this.logger.warn(`Slow request: ${request.url} took ${elapsed}ms`);
        }
      }),
    );
  }
}

// 2. Profile database queries
import { Logger } from '@nestjs/common';

// In TypeORM configuration
{
  logging: ['query', 'error', 'warn'],
  logger: 'advanced-console',
  maxQueryExecutionTime: 1000,  // Log slow queries
}

Sources