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
BaseEntityfrom@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 (
componentReponotcomponentRepository) - Use
repo.merge()thenrepo.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
ParseUUIDPipefor UUID params
See the main statux-api/README.md for complete coding standards.