Skip to Content
πŸš€ APSO is now in public beta. Get started β†’

Project Structure

This guide explains the organization of a generated NestJS project and where to add your custom code.

Directory Layout

my-api/ β”œβ”€β”€ src/ β”‚ β”œβ”€β”€ entities/ # Generated: TypeORM entities β”‚ β”œβ”€β”€ modules/ # Generated: NestJS modules β”‚ β”œβ”€β”€ extensions/ # Your code: Custom additions β”‚ β”œβ”€β”€ common/ # Generated: Shared utilities β”‚ β”œβ”€β”€ config/ # Configuration files β”‚ β”œβ”€β”€ migrations/ # Database migrations β”‚ β”œβ”€β”€ app.module.ts # Root module β”‚ └── main.ts # Application entry point β”œβ”€β”€ test/ # Test files β”œβ”€β”€ .apsorc # APSO schema definition └── package.json

Generated Code

Entities (src/entities/)

TypeORM entity definitions:

// src/entities/project.entity.ts import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm'; import { Task } from './task.entity'; @Entity('projects') export class Project { @PrimaryGeneratedColumn('uuid') id: string; @Column() name: string; @Column({ nullable: true }) description: string; @Column() organizationId: string; @OneToMany(() => Task, task => task.project) tasks: Task[]; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) createdAt: Date; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) updatedAt: Date; }

Modules (src/modules/)

Each entity gets its own module:

src/modules/projects/ β”œβ”€β”€ projects.module.ts # Module definition β”œβ”€β”€ projects.controller.ts # HTTP endpoints β”œβ”€β”€ projects.service.ts # Business logic └── dto/ β”œβ”€β”€ create-project.dto.ts └── update-project.dto.ts

Controller Example

// src/modules/projects/projects.controller.ts @Controller('projects') @UseGuards(AuthGuard) export class ProjectsController { constructor(private readonly projectsService: ProjectsService) {} @Get() @ApiOperation({ summary: 'Get all projects' }) findAll( @Query() query: FindAllQuery, @CurrentUser() user: User, ) { return this.projectsService.findAll(query, user); } @Post() @ApiOperation({ summary: 'Create a project' }) create( @Body() dto: CreateProjectDto, @CurrentUser() user: User, ) { return this.projectsService.create(dto, user); } }

Service Example

// src/modules/projects/projects.service.ts @Injectable() export class ProjectsService { constructor( @InjectRepository(Project) private readonly repository: Repository<Project>, ) {} async findAll(query: FindAllQuery, user: User) { return this.repository.find({ where: { organizationId: user.organizationId }, take: query.limit, skip: query.offset, }); } async create(dto: CreateProjectDto, user: User) { const project = this.repository.create({ ...dto, organizationId: user.organizationId, }); return this.repository.save(project); } }

Common (src/common/)

Shared utilities and decorators:

src/common/ β”œβ”€β”€ decorators/ β”‚ β”œβ”€β”€ current-user.decorator.ts β”‚ └── organization.decorator.ts β”œβ”€β”€ guards/ β”‚ β”œβ”€β”€ auth.guard.ts β”‚ └── organization.guard.ts β”œβ”€β”€ interceptors/ β”‚ └── transform.interceptor.ts └── filters/ └── http-exception.filter.ts

Extension Points

Extensions Directory (src/extensions/)

Add your custom code here. It won’t be overwritten during regeneration.

src/extensions/ β”œβ”€β”€ controllers/ # Custom endpoints β”‚ └── reports.controller.ts β”œβ”€β”€ services/ # Custom services β”‚ └── email.service.ts β”œβ”€β”€ modules/ # Complete modules β”‚ └── analytics/ └── index.ts # Exports

Adding Custom Endpoints

// src/extensions/controllers/reports.controller.ts import { Controller, Get, UseGuards } from '@nestjs/common'; import { AuthGuard } from '../../common/guards/auth.guard'; @Controller('reports') @UseGuards(AuthGuard) export class ReportsController { @Get('summary') async getSummary() { // Your custom logic return { total: 100, active: 75 }; } }

Register in extensions module:

// src/extensions/index.ts import { Module } from '@nestjs/common'; import { ReportsController } from './controllers/reports.controller'; @Module({ controllers: [ReportsController], providers: [], }) export class ExtensionsModule {}

Configuration Files

Root Module

// src/app.module.ts @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forRoot(databaseConfig), ProjectsModule, TasksModule, ExtensionsModule, // Your custom module ], }) export class AppModule {}

Main Entry Point

// src/main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api/v1'); app.enableCors(); const config = new DocumentBuilder() .setTitle('My API') .setVersion('1.0') .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api/docs', app, document); await app.listen(process.env.PORT || 3001); } bootstrap();

File Naming Conventions

TypePatternExample
Entity{name}.entity.tsproject.entity.ts
Module{name}.module.tsprojects.module.ts
Controller{name}.controller.tsprojects.controller.ts
Service{name}.service.tsprojects.service.ts
DTO{action}-{name}.dto.tscreate-project.dto.ts
Guard{name}.guard.tsauth.guard.ts
Decorator{name}.decorator.tscurrent-user.decorator.ts
Last updated on