Skip to content
Back to skills

Nestjs Expert

ASecurity

Provides NestJS patterns for modules, controllers, providers, guards, interceptors, and microservices. Use when working with NestJS TypeScript files (*.module.ts, *.controller.ts, *.service.ts) or when the user mentions NestJS, Nest.js, or NestJS modules.

  • 71 stars
  • 0 votes
  • 0 copies
  • 8 views
  • Added May 27, 2026
data-aitypescriptbashnodeexpressdockergitapidatabasebackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add tranhieutt/software_development_department --skill nestjs-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nestjs Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nestjs Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tranhieutt-nestjs-expert/badge)](https://www.skillsdirectory.com/skills/tranhieutt-nestjs-expert)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nestjs-expert
type: reference
description: "Provides NestJS patterns for modules, controllers, providers, guards, interceptors, and microservices. Use when working with NestJS TypeScript files (*.module.ts, *.controller.ts, *.service.ts) or when the user mentions NestJS, Nest.js, or NestJS modules."
paths: ["**/*.module.ts", "**/*.controller.ts", "**/*.service.ts", "**/*.guard.ts", "**/nest-cli.json"]
effort: 3
allowed-tools: Read, Glob, Grep, Write, Edit, Bash
user-invocable: true
when_to_use: "When building NestJS backend APIs, microservices, or guards/interceptors"
---

# NestJS Expert

## Critical rules (non-obvious)

- **Circular dependencies**: use `forwardRef(() => ServiceB)` in both modules; better — restructure to avoid
- **Global modules**: use `@Global()` sparingly; prefer explicit imports to keep modules testable
- **`APP_GUARD` / `APP_INTERCEPTOR`**: registered in AppModule providers, not in individual modules
- **Lifecycle hooks order**: `onModuleInit` → `onApplicationBootstrap` → ready; `onModuleDestroy` → `beforeApplicationShutdown` → `onApplicationShutdown`
- **Never use `req.user` without type assertion** — it's `any` from Passport; extend `Express.Request`

## Module structure

```typescript
@Module({
  imports: [TypeOrmModule.forFeature([User]), JwtModule],
  controllers: [UserController],
  providers: [UserService, UserRepository],
  exports: [UserService],   // export what other modules need
})
export class UserModule {}
```

## Controller with validation

```typescript
@Controller("users")
@UseGuards(JwtAuthGuard)
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(":id")
  @HttpCode(HttpStatus.OK)
  async findOne(@Param("id", ParseUUIDPipe) id: string, @CurrentUser() user: User) {
    return this.userService.findOneOrFail(id);
  }

  @Post()
  @Roles(Role.ADMIN)
  async create(@Body() dto: CreateUserDto) {
    return this.userService.create(dto);
  }
}
```

## Service with repository pattern

```typescript
@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User) private readonly repo: Repository<User>,
    private readonly eventEmitter: EventEmitter2,
  ) {}

  async findOneOrFail(id: string): Promise<User> {
    const user = await this.repo.findOne({ where: { id } });
    if (!user) throw new NotFoundException(`User ${id} not found`);
    return user;
  }

  async create(dto: CreateUserDto): Promise<User> {
    const user = this.repo.create(dto);
    const saved = await this.repo.save(user);
    this.eventEmitter.emit("user.created", new UserCreatedEvent(saved));
    return saved;
  }
}
```

## JWT auth guard pattern

```typescript
@Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }
  handleRequest(err: any, user: any) {
    if (err || !user) throw err ?? new UnauthorizedException();
    return user;
  }
}

// Custom decorator for current user
export const CurrentUser = createParamDecorator(
  (_, ctx: ExecutionContext) => ctx.switchToHttp().getRequest().user,
);
```

## Global exception filter

```typescript
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const status = exception instanceof HttpException
      ? exception.getStatus()
      : HttpStatus.INTERNAL_SERVER_ERROR;
    ctx.getResponse().status(status).json({
      statusCode: status,
      message: exception instanceof HttpException ? exception.message : "Internal server error",
      timestamp: new Date().toISOString(),
    });
  }
}
// Register: app.useGlobalFilters(new AllExceptionsFilter())
```

## Interceptor: response transform + timing

```typescript
@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(ctx: ExecutionContext, next: CallHandler): Observable<any> {
    const start = Date.now();
    return next.handle().pipe(
      map(data => ({ data, duration: Date.now() - start, timestamp: new Date() })),
    );
  }
}
```

## Validation DTO

```typescript
export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsString()
  @MinLength(8)
  @Matches(/^(?=.*[A-Z])(?=.*\d)/, { message: "Must contain uppercase and digit" })
  password: string;

  @IsEnum(Role)
  @IsOptional()
  role?: Role = Role.USER;
}
// Global: app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }))
```

## Config with validation

```typescript
// app.module.ts
ConfigModule.forRoot({
  isGlobal: true,
  validationSchema: Joi.object({
    NODE_ENV: Joi.string().valid("development", "production", "test").required(),
    PORT: Joi.number().default(3000),
    DATABASE_URL: Joi.string().required(),
    JWT_SECRET: Joi.string().min(32).required(),
  }),
})
```

## Common pitfalls

| Pitfall | Fix |
|---|---|
| Injecting service into wrong module | Export service from its module; import module |
| Missing `async` on lifecycle hooks | `async onModuleInit()` if doing DB work on startup |
| `ValidationPipe` without `whitelist: true` | Strips extra fields; prevents mass assignment |
| Blocking the event loop in provider | Use `async/await`; never synchronous I/O |
| Missing `enableShutdownHooks()` | Required for `onModuleDestroy` to fire in Docker |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.