Skip to Content
🚀 APSO is now in public beta. Get started →

Custom Endpoints

While APSO generates standard CRUD endpoints, you’ll often need custom endpoints for specific business logic. This guide shows how to add them.

Using the Extensions Pattern

Add custom controllers in src/extensions/controllers/:

// src/extensions/controllers/analytics.controller.ts import { Controller, Get, UseGuards } from '@nestjs/common'; import { AuthGuard } from '../../common/guards/auth.guard'; import { CurrentUser } from '../../common/decorators/current-user.decorator'; import { User } from '../../entities/user.entity'; @Controller('analytics') @UseGuards(AuthGuard) export class AnalyticsController { @Get('dashboard') async getDashboard(@CurrentUser() user: User) { return { projectCount: 10, taskCount: 45, completedTasks: 30, }; } }

Controller Decorators

HTTP Methods

import { Get, Post, Put, Patch, Delete } from '@nestjs/common'; @Get('items') // GET /items @Post('items') // POST /items @Put('items/:id') // PUT /items/:id @Patch('items/:id') // PATCH /items/:id @Delete('items/:id') // DELETE /items/:id

Route Parameters

import { Param, Query, Body, Headers } from '@nestjs/common'; @Get(':id') async getById(@Param('id') id: string) { // Access route parameter } @Get() async search(@Query('q') query: string) { // Access query parameter } @Post() async create(@Body() dto: CreateDto) { // Access request body } @Get() async withAuth(@Headers('authorization') auth: string) { // Access headers }

Response Handling

import { Res, HttpStatus, HttpCode } from '@nestjs/common'; import { Response } from 'express'; @Post() @HttpCode(HttpStatus.CREATED) async create(@Body() dto: CreateDto) { // Returns 201 status } @Get('download') async download(@Res() res: Response) { res.setHeader('Content-Type', 'application/pdf'); res.send(pdfBuffer); }

Adding Custom Services

Create services for business logic:

// src/extensions/services/reports.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Project } from '../../entities/project.entity'; import { Task } from '../../entities/task.entity'; @Injectable() export class ReportsService { constructor( @InjectRepository(Project) private projectRepo: Repository<Project>, @InjectRepository(Task) private taskRepo: Repository<Task>, ) {} async getProjectSummary(organizationId: string) { const [projects, projectCount] = await this.projectRepo.findAndCount({ where: { organizationId }, }); const taskCount = await this.taskRepo.count({ where: { project: { organizationId } }, }); return { projectCount, taskCount, projects: projects.slice(0, 5), }; } }

Use in controllers:

// src/extensions/controllers/reports.controller.ts @Controller('reports') @UseGuards(AuthGuard) export class ReportsController { constructor(private readonly reportsService: ReportsService) {} @Get('summary') async getSummary(@CurrentUser() user: User) { return this.reportsService.getProjectSummary(user.organizationId); } }

Registering Extensions

Register your custom code in the extensions module:

// src/extensions/index.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Project } from '../entities/project.entity'; import { Task } from '../entities/task.entity'; import { AnalyticsController } from './controllers/analytics.controller'; import { ReportsController } from './controllers/reports.controller'; import { ReportsService } from './services/reports.service'; @Module({ imports: [ TypeOrmModule.forFeature([Project, Task]), ], controllers: [ AnalyticsController, ReportsController, ], providers: [ ReportsService, ], }) export class ExtensionsModule {}

Validation

Add validation to DTOs:

// src/extensions/dto/generate-report.dto.ts import { IsDateString, IsEnum, IsOptional } from 'class-validator'; enum ReportFormat { PDF = 'pdf', CSV = 'csv', JSON = 'json', } export class GenerateReportDto { @IsDateString() startDate: string; @IsDateString() endDate: string; @IsEnum(ReportFormat) @IsOptional() format?: ReportFormat = ReportFormat.JSON; }

Use in controller:

@Post('generate') async generate(@Body() dto: GenerateReportDto) { // dto is validated automatically }

Authentication & Authorization

Using Guards

import { UseGuards } from '@nestjs/common'; import { AuthGuard } from '../../common/guards/auth.guard'; import { RolesGuard } from '../../common/guards/roles.guard'; import { Roles } from '../../common/decorators/roles.decorator'; @Controller('admin') @UseGuards(AuthGuard, RolesGuard) export class AdminController { @Get('users') @Roles('admin') async getUsers() { // Only admins can access } }

Creating Custom Guards

// src/extensions/guards/api-key.guard.ts import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; @Injectable() export class ApiKeyGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest(); const apiKey = request.headers['x-api-key']; return this.validateApiKey(apiKey); } private validateApiKey(key: string): boolean { // Your validation logic return key === process.env.INTERNAL_API_KEY; } }

Swagger Documentation

Document your endpoints:

import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger'; @Controller('reports') @ApiTags('Reports') @ApiBearerAuth() export class ReportsController { @Get('summary') @ApiOperation({ summary: 'Get project summary' }) @ApiResponse({ status: 200, description: 'Summary data' }) @ApiResponse({ status: 401, description: 'Unauthorized' }) async getSummary() { // ... } }

Error Handling

Throw appropriate exceptions:

import { NotFoundException, BadRequestException, ForbiddenException, } from '@nestjs/common'; @Get(':id') async getReport(@Param('id') id: string, @CurrentUser() user: User) { const report = await this.reportsService.findOne(id); if (!report) { throw new NotFoundException('Report not found'); } if (report.organizationId !== user.organizationId) { throw new ForbiddenException('Access denied'); } return report; }
Last updated on