debug:nestjs
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
providersarray - 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-transformerorclass-validatorpackages - 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
- NestJS Official Documentation - Common Errors
- NestJS Official Documentation - Exception Filters
- NestJS Official Documentation - Logger
- Debugging NestJS in VSCode - Plain English
- Debugging NestJS Applications in VS Code - Medium
- Step-debugging Docker Compose NestJS services - Rob Allen
- NestJS Error Handling Patterns - Better Stack
- Error Handling in NestJS Best Practices - DEV Community
- 10 Common Mistakes to Avoid in NestJS - Medium
- Debugging Nest.js Applications - Rookout
More from snakeo/claude-debug-and-refactor-skills-plugin
refactor:nestjs
Refactor NestJS/TypeScript code to improve maintainability, readability, and adherence to best practices. Identifies and fixes circular dependencies, god object services, fat controllers with business logic, deep nesting, and SRP violations. Applies NestJS patterns including proper module organization, provider scopes, custom decorators, guards, interceptors, pipes, DTOs with class-validator, exception filters, CQRS, repository pattern, and event-driven architecture. Transforms code into exemplary implementations following SOLID principles.
42refactor:nuxtjs
Refactor Nuxt.js/Vue code to improve maintainability, readability, and adherence to best practices. Identifies and fixes DRY violations, oversized components, deep nesting, SRP violations, data fetching anti-patterns with useFetch/useAsyncData/$fetch, poor composable organization, and mixed business/presentation logic. Applies Nuxt 3 patterns including auto-imports, proper data fetching, single-responsibility composables, TypeScript integration, runtime config, Nitro server routes, Nuxt Layers, middleware patterns, Pinia state management, and performance optimizations.
35refactor:nextjs
Refactor Next.js code to improve maintainability, readability, and adherence to App Router best practices. Identifies and fixes God Components, prop drilling, inappropriate 'use client' usage, outdated Pages Router patterns, missing Suspense boundaries, incorrect caching strategies, and useEffect data fetching anti-patterns. Applies modern Next.js 15 patterns including Server Components, Client Components, Server Actions, streaming with Suspense, proper caching strategies, Container-Presentational pattern, layout composition, parallel routes, and intercepting routes.
33refactor:angular
Refactor Angular code to improve maintainability, readability, and adherence to best practices. Transforms large components, nested subscriptions, and outdated patterns into clean, modern Angular code. Applies signals, standalone components, OnPush change detection, proper RxJS patterns, and Angular Style Guide conventions. Identifies and fixes memory leaks, function calls in templates, fat components, and missing lazy loading.
33debug:react-native
Debug React Native issues systematically. Use when encountering native module errors like "Native module cannot be null", Metro bundler issues including port conflicts and cache corruption, platform-specific build failures for iOS CocoaPods or Android Gradle, bridge communication problems, Hermes engine bytecode compilation failures, red screen fatal errors, or New Architecture migration issues with TurboModules and Fabric renderer.
32refactor:express
Refactor Express.js/Node.js code to improve maintainability, readability, and adherence to best practices. Transforms callback hell, fat route handlers, and outdated patterns into clean, modern JavaScript/TypeScript code. Applies async/await, controller-service-repository architecture, proper middleware patterns, and ESM modules. Identifies and fixes anti-patterns including blocking event loop, improper error handling, forEach with async callbacks, and memory leaks.
30