Skip to main content
rule

nestjs · sanjeed5 — Cursor rule: NestJS structure and types

A Cursor rule that sets NestJS project structure: feature modules, explicit types, validated DTOs and global exception filters.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/nestjs.mdc
Open on GitHub

What nestjs does

The rule lays out how to organise a NestJS codebase: every business domain gets its own module instead of a shared controllers or services folder. Inside a module you keep controllers, services, repositories and DTOs in separate directories. Files and folders use kebab-case, classes PascalCase, interfaces an I prefix, constants UPPER_SNAKE_CASE.

Types get their own section. Variables, parameters and return values must be explicit, and any is off the table. Strict TypeScript goes into tsconfig.json. Incoming request bodies and query params go through DTOs with class-validator decorators such as @IsString, @IsEmail and @MinLength. Dependencies are injected through the constructor, so new on a service breaks the pattern.

Errors are handled in one place: a class with @Catch(HttpException) formats the response, and main.ts registers it with app.useGlobalFilters. No try/catch in every method. Route access is closed off by a guard that reads roles through Reflector. Worth knowing before you drop it in: the examples assume a NestJS app in TypeScript, and the DTO snippets need class-validator installed.

What it helps with

  • split code into feature modules instead of shared controllers and services folders
  • drop any and add explicit types across services
  • validate request bodies with class-validator DTOs
  • return one consistent error shape instead of try/catch in every method
  • protect routes with a role-checking guard

Example prompts for the agent

  • Restructure the users module by feature: controller, service, repository and DTO in separate folders
  • Add a create-user DTO with class-validator instead of an any body
  • Register the global HttpExceptionFilter in main.ts and remove try/catch from the controllers

How to install nestjs

Manually: put the files in place

.cursor/rules/nestjs.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about nestjs

How do I install the nestjs rule in Cursor?

Create .cursor/rules/nestjs.mdc in your project root and paste the rule contents into it.

What does the nestjs rule do?

It defines NestJS layout and conventions: feature modules, naming rules, explicit types, validated DTOs, a global exception filter and guards for roles.

What do I need for the rule's examples to work?

A NestJS project in TypeScript with strict mode in tsconfig.json. The DTO examples also need class-validator.

Source text

nestjs.mdc · CC0-1.0
---
description: This guide provides opinionated, actionable best practices for building robust, scalable, and maintainable NestJS applications using TypeScript, emphasizing modern patterns and common pitfalls.
globs: **/*.{ts,tsx}
---
# NestJS Best Practices

NestJS is a powerful framework for building scalable Node.js applications. Adhering to these guidelines ensures your projects are maintainable, performant, and secure.

## 1. Code Organization and Structure

Adopt a feature-based, semi-hexagonal architecture. Each business domain gets its own module.

### ✅ GOOD: Feature-based Modules
Organize code by feature, not by type.

```typescript
// src/modules/users/users.module.ts
@Module({
  controllers: [UsersController],
  providers: [UsersService, UsersRepository],
  exports: [UsersService],
})
export class UsersModule {}
// src/modules/users/controllers/users.controller.ts
// src/modules/users/services/users.service.ts
// src/modules/users/repositories/users.repository.ts
// src/modules/users/dtos/create-user.dto.ts
```

### ❌ BAD: Type-based Flat Structure
Avoid scattering related logic across different top-level folders.

```
// src/controllers/users.controller.ts
// src/services/users.service.ts
```

### Naming Conventions
Follow standard TypeScript conventions for clarity:
*   **Files/Folders**: `kebab-case` (e.g., `user-profile.module.ts`)
*   **Classes**: `PascalCase` (e.g., `UsersService`, `CreateUserDto`)
*   **Interfaces**: `IPascalCase` (e.g., `IUserRepository`)
*   **Functions/Methods**: `camelCase` (e.g., `createUser`, `findAllUsers`)
*   **Constants**: `UPPER_SNAKE_CASE` (e.g., `JWT_SECRET`)

## 2. Type Safety and Strictness

Always use explicit types. Avoid `any`. Enable strict TypeScript in `tsconfig.json`.

### ✅ GOOD: Explicit Types
Every variable, parameter, and return value must have an explicit type.

```typescript
interface IUser { id: string; name: string; email: string; }
async function fetchUserById(id: string): Promise<IUser | null> {
  const user: IUser | undefined = await this.userRepository.findOne(id);
  return user ?? null;
}
```

### ❌ BAD: Implicit `any`
Leads to runtime errors and defeats TypeScript's purpose.

```typescript
function fetchUserById(id) { // id is implicitly 'any'
  const user = this.userRepository.findOne(id);
  return user;
}
```

## 3. Common Patterns

### Dependency Injection (DI)
Leverage NestJS's DI system for loose coupling and testability.

### ✅ GOOD: Constructor Injection
Always inject dependencies via the constructor.

```typescript
@Injectable()
export class UsersService {
  constructor(private readonly userRepository: IUserRepository) {}
  async findAll(): Promise<IUser[]> { return this.userRepository.findAll(); }
}
```

### ❌ BAD: Manual Instantiation
Avoid `new` keyword for services/providers.

```typescript
const userService = new UsersService(); // Breaks DI and testability
```

### Data Transfer Objects (DTOs)
Use DTOs with `class-validator` for all incoming request bodies and query parameters.

### ✅ GOOD: Validated DTOs
Ensure data integrity at the edge of your application.

```typescript
// src/modules/users/dtos/create-user.dto.ts
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
  @IsString() @MinLength(3) name: string;
  @IsEmail() email: string;
  @IsString() @MinLength(8) password: string;
}
// src/modules/users/controllers/users.controller.ts
@Post()
async createUser(@Body() createUserDto: CreateUserDto): Promise<IUser> {
  return this.userService.create(createUserDto);
}
```

### ❌ BAD: Direct Request Body Usage
Unvalidated data is a security risk.

```typescript
@Post()
async createUser(@Body() body: any): Promise<IUser> { /* ... */ }
```

### Controllers and Services
Controllers handle HTTP requests and delegate business logic to services. Services contain the core business logic and interact with data layers.

### ✅ GOOD: Lean Controllers, Rich Services
Controllers should be thin, focusing on request/response.

```typescript
// src/modules/users/controllers/users.controller.ts
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
  @Get(':id')
  async findOne(@Param('id') id: string): Promise<IUser> {
    return this.usersService.findById(id);
  }
}
// src/modules/users/services/users.service.ts
@Injectable()
export class UsersService {
  constructor(private readonly userRepository: IUserRepository) {}
  async findById(id: string): Promise<IUser> {
    const user = await this.userRepository.findOne(id);
    if (!user) { throw new NotFoundException(`User ID "${id}" not found.`); }
    return user;
  }
}
```

### ❌ BAD: Business Logic in Controllers
Makes controllers hard to test and re-use.

```typescript
@Controller('users')
export class UsersController {
  constructor(private readonly userRepository: IUserRepository) {}
  @Get(':id')
  async findOne(@Param('id') id: string): Promise<IUser> {
    const user = await this.userRepository.findOne(id); // Logic here
    if (!user) { throw new NotFoundException(`User ID "${id}" not found.`); }
    return user;
  }
}
```

## 4. Error Handling

Centralize error handling using Exception Filters.

### ✅ GOOD: Global Exception Filters
Catch and format all unhandled exceptions consistently.

```typescript
// src/shared/common/filters/http-exception.filter.ts
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>(); const status = exception.getStatus();
    response.status(status).json({
      statusCode: status, timestamp: new Date().toISOString(), path: request.url,
      message: exception.message,
    });
  }
}
// src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter()); // Apply globally
  await app.listen(3000);
}
```

### ❌ BAD: `try/catch` in Every Method
Leads to boilerplate and inconsistent error responses.

```typescript
@Get(':id')
async findOne(@Param('id') id: string): Promise<IUser> {
  try { return await this.usersService.findById(id); }
  catch (error) { throw new HttpException('Failed', HttpStatus.INTERNAL_SERVER_ERROR); }
}
```

## 5. Security Best Practices

### Guards for Authorization
Use `Guards` for declarative route protection.

### ✅ GOOD: Role-Based Access Control (RBAC)
```typescript
// src/modules/auth/guards/roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}
  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(ROLES_KEY, [
      context.getHandler(), context.getClass(),
    ]);
    if (!requiredRoles) { return true; }
    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.some((role) => user.roles?.includes(role));
  }
}
// src/modules/users/controllers/users.controller.ts
@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard) // Apply guards
export class UsersController {
  @Get() @Roles(UserRole.Admin) // Require Admin role
  async findAll(): Promise<IUser[]> { return this.usersService.findAll(); }
}
```

### Environment Variables
Manage configuration securely using `@nestjs/config`.

### ✅ GOOD: `ConfigModule`
```typescript
// src/app.module.ts
@Module({
  imports: [ConfigModule.forRoot({ isGlobal: true, envFilePath: `.env.${process.env.NODE_ENV}` })],
})
export class AppModule {}
// In a service
@Injectable()
export class DatabaseService {
  constructor(private configService: ConfigService) {
    const dbHost = this.configService.get<string>('DATABASE_HOST');
  }
}
```

### ❌ BAD: Hardcoding Secrets
Never hardcode sensitive information.

```typescript
const JWT_SECRET = 'super-secret-key'; // BAD
```

## 6. Performance Considerations

### Fastify Adapter
Use Fastify for improved performance over Express.

### ✅ GOOD: Fastify Integration
```typescript
// src/main.ts
async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule, new FastifyAdapter() // Use Fastify
  );
  await app.listen(3000);
}
bootstrap();
```

### Caching
Implement caching for frequently accessed, slow-changing data.

### ✅ GOOD: `CacheModule`
```typescript
// src/app.module.ts
@Module({
  imports: [CacheModule.register({ store: redisStore, host: 'localhost', port: 6379, ttl: 300 })],
})
export class AppModule {}
// In a service method
@Get() @UseInterceptors(CacheInterceptor) @CacheKey('all_users') @CacheTTL(60)
async findAll(): Promise<IUser[]> { return this.usersService.findAll(); }
```

## 7. Testing

Prioritize unit tests for services and e2e tests for controllers.

### ✅ GOOD: Unit Testing Services
Mock external

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Backend and APIs →

nestjs by other authors comes first.

nestjs official github This rule feeds GitHub Copilot a set of NestJS conventions: modules, DI, DTOs and validation. Useful when the agent writes TypeScript server code. ★ 40,418repo stars nestjs-best-practices giuseppe-trisciuoglio A skill that hands your agent a rulebook for NestJS: module boundaries, provider scopes, exception filters, DTO validation and Drizzle ORM. CX Codex +1 3,800installs nestjs-expert https://github.com/Jeffallan A skill for designing and building NestJS backends in TypeScript: modules, services, controllers, validation, authentication and testing. Use it when building REST APIs or GraphQL services with enterprise patterns. CX Codex +1 5,900installs nestjs-patterns affaan-m Architectural guidelines for NestJS backends, covering modular directory layouts, DTO validation, and testing. CX Codex +1 5,000installs nestjs-code-review giuseppe-trisciuoglio A skill that reviews NestJS apps: controllers, services, modules, DI, guards and database patterns. Useful before merging a pull request. CX Codex +1 3,800installs nestjs-modular-monolith Felipe Rodrigues - github.com/felipfr Takes an agent from domain analysis to code: module boundaries, DDD, Clean Architecture and CQRS in a NestJS backend. CX Codex +1 329installs openapi-to-application-nodejs-nestjs official Awesome Copilot Community Builds a NestJS application from an OpenAPI spec: project scaffolding, controllers, services. Useful when the API contract exists but the code does not. ★ 40,418repo stars

From the same set69

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor