Skip to main content

API Coding Standards

Entity Pattern

@Entity({ schema: 'statuspages', name: 'components' })
export class Component extends BaseEntity {
@Column({ name: 'project_id' })
@Index()
projectId: string;

@Column({ name: 'display_name', length: 100 })
displayName: string;

@Column({ name: 'is_active', default: true })
isActive: boolean;
}

Key rules:

  • Extend BaseEntity from @app/database
  • Use explicit { name: 'snake_case' } for all columns
  • Add @Index() on foreign keys
  • Export enums from the same file

Service Pattern

@Injectable()
export class ComponentsService {
private readonly logger = new Logger(ComponentsService.name);

constructor(
@InjectRepository(Component)
private readonly componentRepo: Repository<Component>,
) {}

async update(id: string, dto: UpdateComponentDto): Promise<Component> {
const component = await this.componentRepo.findOne({ where: { id } });
if (!component) throw new NotFoundException('Component not found');

this.componentRepo.merge(component, dto);
return this.componentRepo.save(component);
}
}

Key rules:

  • Private readonly logger
  • Short repo variable names (componentRepo not componentRepository)
  • Use repo.merge() then repo.save() for updates
  • Throw NotFoundException, ForbiddenException, etc.

Controller Pattern

@ApiTags('Components')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard, ProjectAccessGuard)
@Controller('projects/:projectId/components')
export class ComponentsController {
@Get(':id')
@ApiOperation({ summary: 'Get component by ID' })
findOne(
@Param('id', ParseUUIDPipe) id: string,
@CurrentProject() project: Project,
): Promise<Component> {
return this.service.findOne(id, project.id);
}
}

Key rules:

  • @ApiTags(), @ApiBearerAuth(), appropriate guards
  • @ApiOperation({ summary: '...' }) on every endpoint
  • Explicit return types
  • Use ParseUUIDPipe for UUID params

See the main statux-api/README.md for complete coding standards.