Production-ready NestJS project structure with modules, guards, interceptors, and enterprise patterns. Use when scaffolding, structuring, or architecting nestjs projects.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill nestjs-project-architect --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Nestjs Project Architect?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-nestjs-project-architect)More formats (shields.io, HTML) on the badges page.
---
name: nestjs-project-architect
description: Production-ready NestJS project structure with modules, guards, interceptors, and enterprise patterns. Use when scaffolding, structuring, or architecting nestjs projects.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: project-structure
tags: [nestjs, nodejs, typescript, api, project-structure, enterprise]
---
# NestJS Project Architect Mode
You are an expert in structuring production-ready NestJS applications with modular architecture, proper dependency injection, and enterprise patterns.
## Project Structure
```text
nestjs-project/
├── src/
│ ├── main.ts # Application entry point
│ ├── app.module.ts # Root module
│ │
│ ├── common/
│ │ ├── decorators/
│ │ │ ├── current-user.decorator.ts
│ │ │ └── roles.decorator.ts
│ │ ├── filters/
│ │ │ ├── http-exception.filter.ts
│ │ │ └── all-exceptions.filter.ts
│ │ ├── guards/
│ │ │ ├── jwt-auth.guard.ts
│ │ │ └── roles.guard.ts
│ │ ├── interceptors/
│ │ │ ├── logging.interceptor.ts
│ │ │ ├── transform.interceptor.ts
│ │ │ └── timeout.interceptor.ts
│ │ ├── pipes/
│ │ │ └── validation.pipe.ts
│ │ ├── middleware/
│ │ │ └── logger.middleware.ts
│ │ └── interfaces/
│ │ └── index.ts
│ │
│ ├── config/
│ │ ├── configuration.ts
│ │ ├── database.config.ts
│ │ └── validation.schema.ts
│ │
│ ├── database/
│ │ ├── database.module.ts
│ │ ├── migrations/
│ │ └── seeds/
│ │
│ ├── modules/
│ │ ├── auth/
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── strategies/
│ │ │ │ ├── jwt.strategy.ts
│ │ │ │ └── local.strategy.ts
│ │ │ └── dto/
│ │ │ ├── login.dto.ts
│ │ │ └── register.dto.ts
│ │ │
│ │ ├── users/
│ │ │ ├── users.module.ts
│ │ │ ├── users.controller.ts
│ │ │ ├── users.service.ts
│ │ │ ├── users.repository.ts
│ │ │ ├── entities/
│ │ │ │ └── user.entity.ts
│ │ │ └── dto/
│ │ │ ├── create-user.dto.ts
│ │ │ └── update-user.dto.ts
│ │ │
│ │ └── items/
│ │ ├── items.module.ts
│ │ ├── items.controller.ts
│ │ ├── items.service.ts
│ │ ├── entities/
│ │ │ └── item.entity.ts
│ │ └── dto/
│ │ ├── create-item.dto.ts
│ │ └── update-item.dto.ts
│ │
│ └── shared/
│ ├── shared.module.ts
│ └── services/
│ ├── cache.service.ts
│ └── email.service.ts
│
├── test/
│ ├── app.e2e-spec.ts
│ ├── jest-e2e.json
│ └── utils/
│ └── test-helper.ts
│
├── docker/
│ ├── Dockerfile
│ └── docker-compose.yml
│
├── .env.example
├── .eslintrc.js
├── .prettierrc
├── nest-cli.json
├── package.json
├── tsconfig.json
├── tsconfig.build.json
└── README.md
```
## Core Files
```typescript
// src/main.ts
import { NestFactory } from "@nestjs/core";
import { ValidationPipe, VersioningType } from "@nestjs/common";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { ConfigService } from "@nestjs/config";
import helmet from "helmet";
import { AppModule } from "./app.module";
import { HttpExceptionFilter } from "./common/filters/http-exception.filter";
import { LoggingInterceptor } from "./common/interceptors/logging.interceptor";
import { TransformInterceptor } from "./common/interceptors/transform.interceptor";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
// Security
app.use(helmet());
app.enableCors({
origin: configService.get("CORS_ORIGINS")?.split(",") || [],
credentials: true,
});
// Versioning
app.enableVersioning({
type: VersioningType.URI,
defaultVersion: "1",
});
// Global pipes, filters, interceptors
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
transformOptions: { enableImplicitConversion: true },
}),
);
app.useGlobalFilters(new HttpExceptionFilter());
app.useGlobalInterceptors(new LoggingInterceptor(), new TransformInterceptor());
// Swagger
const config = new DocumentBuilder()
.setTitle("API")
.setDescription("API Documentation")
.setVersion("1.0")
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("docs", app, document);
const port = configService.get("PORT") || 3000;
await app.listen(port);
}
bootstrap();
```
```typescript
// src/app.module.ts
import { Module, MiddlewareConsumer } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { TypeOrmModule } from "@nestjs/typeorm";
import configuration from "./config/configuration";
import { DatabaseConfig } from "./config/database.config";
import { LoggerMiddleware } from "./common/middleware/logger.middleware";
import { AuthModule } from "./modules/auth/auth.module";
import { UsersModule } from "./modules/users/users.module";
import { ItemsModule } from "./modules/items/items.module";
import { SharedModule } from "./shared/shared.module";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
}),
TypeOrmModule.forRootAsync({
useClass: DatabaseConfig,
}),
AuthModule,
UsersModule,
ItemsModule,
SharedModule,
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes("*");
}
}
```
```typescript
// src/modules/users/users.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
import { UsersRepository } from "./users.repository";
import { User } from "./entities/user.entity";
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService, UsersRepository],
exports: [UsersService],
})
export class UsersModule {}
```
```typescript
// src/modules/users/users.controller.ts
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, ParseIntPipe } from "@nestjs/common";
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from "@nestjs/swagger";
import { JwtAuthGuard } from "../../common/guards/jwt-auth.guard";
import { RolesGuard } from "../../common/guards/roles.guard";
import { Roles } from "../../common/decorators/roles.decorator";
import { CurrentUser } from "../../common/decorators/current-user.decorator";
import { UsersService } from "./users.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { User } from "./entities/user.entity";
@ApiTags("Users")
@ApiBearerAuth()
@UseGuards(JwtAuthGuard, RolesGuard)
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
@ApiOperation({ summary: "Get all users" })
@Roles("admin")
async findAll(@Query("page") page = 1, @Query("limit") limit = 10) {
return this.usersService.findAll({ page, limit });
}
@Get("me")
@ApiOperation({ summary: "Get current user" })
async getMe(@CurrentUser() user: User) {
return user;
}
@Get(":id")
@ApiOperation({ summary: "Get user by ID" })
async findOne(@Param("id", ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
@Post()
@ApiOperation({ summary: "Create user" })
@Roles("admin")
async create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Put(":id")
@ApiOperation({ summary: "Update user" })
async update(@Param("id", ParseIntPipe) id: number, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(id, updateUserDto);
}
@Delete(":id")
@ApiOperation({ summary: "Delete user" })
@Roles("admin")
async remove(@Param("id", ParseIntPipe) id: number) {
return this.usersService.remove(id);
}
}
```
```typescript
// src/modules/users/users.service.ts
import { Injectable, NotFoundException } from "@nestjs/common";
import { UsersRepository } from "./users.repository";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
constructor(private readonly usersRepository: UsersRepository) {}
async findAll(options: { page: number; limit: number }) {
return this.usersRepository.findWithPagination(options);
}
async findOne(id: number) {
const user = await this.usersRepository.findById(id);
if (!user) {
throw new NotFoundException(`User #${id} not found`);
}
return user;
}
async findByEmail(email: string) {
return this.usersRepository.findByEmail(email);
}
async create(createUserDto: CreateUserDto) {
return this.usersRepository.create(createUserDto);
}
async update(id: number, updateUserDto: UpdateUserDto) {
await this.findOne(id);
return this.usersRepository.update(id, updateUserDto);
}
async remove(id: number) {
await this.findOne(id);
return this.usersRepository.delete(id);
}
}
```
```typescript
// src/common/guards/jwt-auth.guard.ts
import { Injectable, ExecutionContext } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { Reflector } from "@nestjs/core";
@Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>("isPublic", [context.getHandler(), context.getClass()]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
```
```typescript
// src/common/interceptors/transform.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
export interface Response<T> {
data: T;
meta?: Record<string, any>;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(
map((data) => ({
data,
timestamp: new Date().toISOString(),
})),
);
}
}
```
## Best Practices
- One module per feature/domain
- Use DTOs with class-validator for input validation
- Implement repository pattern for data access
- Use guards for authentication/authorization
- Use interceptors for cross-cutting concerns
- Use custom decorators for clean controller code
- Version your API from day one
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!