Massive Dependency Modernization Project
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 3m58s
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 3m58s
This commit is contained in:
@@ -4,22 +4,24 @@ Common code patterns extracted from Architecture Decision Records (ADRs). Use th
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Pattern | Key Function/Class | Import From |
|
||||
| ------------------ | ------------------------------------------------- | ------------------------------------- |
|
||||
| Error Handling | `handleDbError()`, `NotFoundError` | `src/services/db/errors.db.ts` |
|
||||
| Repository Methods | `get*`, `find*`, `list*` | `src/services/db/*.db.ts` |
|
||||
| API Responses | `sendSuccess()`, `sendPaginated()`, `sendError()` | `src/utils/apiResponse.ts` |
|
||||
| Transactions | `withTransaction()` | `src/services/db/connection.db.ts` |
|
||||
| Validation | `validateRequest()` | `src/middleware/validation.ts` |
|
||||
| Authentication | `authenticateJWT` | `src/middleware/auth.ts` |
|
||||
| Caching | `cacheService` | `src/services/cache.server.ts` |
|
||||
| Background Jobs | Queue classes | `src/services/queues.server.ts` |
|
||||
| Feature Flags | `isFeatureEnabled()`, `useFeatureFlag()` | `src/services/featureFlags.server.ts` |
|
||||
| Pattern | Key Function/Class | Import From |
|
||||
| -------------------- | ------------------------------------------------- | ------------------------------------- |
|
||||
| **tsoa Controllers** | `BaseController`, `@Route`, `@Security` | `src/controllers/base.controller.ts` |
|
||||
| Error Handling | `handleDbError()`, `NotFoundError` | `src/services/db/errors.db.ts` |
|
||||
| Repository Methods | `get*`, `find*`, `list*` | `src/services/db/*.db.ts` |
|
||||
| API Responses | `sendSuccess()`, `sendPaginated()`, `sendError()` | `src/utils/apiResponse.ts` |
|
||||
| Transactions | `withTransaction()` | `src/services/db/connection.db.ts` |
|
||||
| Validation | `validateRequest()` | `src/middleware/validation.ts` |
|
||||
| Authentication | `authenticateJWT`, `@Security('bearerAuth')` | `src/middleware/auth.ts` |
|
||||
| Caching | `cacheService` | `src/services/cache.server.ts` |
|
||||
| Background Jobs | Queue classes | `src/services/queues.server.ts` |
|
||||
| Feature Flags | `isFeatureEnabled()`, `useFeatureFlag()` | `src/services/featureFlags.server.ts` |
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [tsoa Controllers](#tsoa-controllers)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Repository Patterns](#repository-patterns)
|
||||
- [API Response Patterns](#api-response-patterns)
|
||||
@@ -32,6 +34,159 @@ Common code patterns extracted from Architecture Decision Records (ADRs). Use th
|
||||
|
||||
---
|
||||
|
||||
## tsoa Controllers
|
||||
|
||||
**ADR**: [ADR-018](../adr/0018-api-documentation-strategy.md), [ADR-059](../adr/0059-dependency-modernization.md)
|
||||
|
||||
All API endpoints are implemented as tsoa controller classes that extend `BaseController`. This pattern provides type-safe OpenAPI documentation generation and standardized response formatting.
|
||||
|
||||
### Basic Controller Structure
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Route,
|
||||
Tags,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Path,
|
||||
Query,
|
||||
Security,
|
||||
SuccessResponse,
|
||||
Response,
|
||||
} from 'tsoa';
|
||||
import type { Request as ExpressRequest } from 'express';
|
||||
import {
|
||||
BaseController,
|
||||
SuccessResponse as SuccessResponseType,
|
||||
ErrorResponse,
|
||||
} from './base.controller';
|
||||
|
||||
interface CreateItemRequest {
|
||||
name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface ItemResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@Route('items')
|
||||
@Tags('Items')
|
||||
export class ItemController extends BaseController {
|
||||
/**
|
||||
* Get an item by ID.
|
||||
* @summary Get item
|
||||
* @param id Item ID
|
||||
*/
|
||||
@Get('{id}')
|
||||
@SuccessResponse(200, 'Item retrieved')
|
||||
@Response<ErrorResponse>(404, 'Item not found')
|
||||
public async getItem(@Path() id: number): Promise<SuccessResponseType<ItemResponse>> {
|
||||
const item = await itemService.getItemById(id);
|
||||
return this.success(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item. Requires authentication.
|
||||
* @summary Create item
|
||||
*/
|
||||
@Post()
|
||||
@Security('bearerAuth')
|
||||
@SuccessResponse(201, 'Item created')
|
||||
@Response<ErrorResponse>(401, 'Not authenticated')
|
||||
public async createItem(
|
||||
@Body() body: CreateItemRequest,
|
||||
@Request() request: ExpressRequest,
|
||||
): Promise<SuccessResponseType<ItemResponse>> {
|
||||
const user = request.user as UserProfile;
|
||||
const item = await itemService.createItem(body, user.user.user_id);
|
||||
return this.created(item);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### BaseController Response Helpers
|
||||
|
||||
```typescript
|
||||
// Success response (200)
|
||||
return this.success(data);
|
||||
|
||||
// Created response (201)
|
||||
return this.created(data);
|
||||
|
||||
// Paginated response
|
||||
const { page, limit } = this.normalizePagination(queryPage, queryLimit);
|
||||
return this.paginated(items, { page, limit, total });
|
||||
|
||||
// Message-only response
|
||||
return this.message('Operation completed');
|
||||
|
||||
// No content (204)
|
||||
return this.noContent();
|
||||
```
|
||||
|
||||
### Authentication with @Security
|
||||
|
||||
```typescript
|
||||
import { Security, Request } from 'tsoa';
|
||||
import { requireAdminRole } from '../middleware/tsoaAuthentication';
|
||||
|
||||
// Require authentication
|
||||
@Get('profile')
|
||||
@Security('bearerAuth')
|
||||
public async getProfile(@Request() req: ExpressRequest): Promise<...> {
|
||||
const user = req.user as UserProfile;
|
||||
return this.success(user);
|
||||
}
|
||||
|
||||
// Require admin role
|
||||
@Delete('users/{id}')
|
||||
@Security('bearerAuth')
|
||||
public async deleteUser(@Path() id: string, @Request() req: ExpressRequest): Promise<void> {
|
||||
requireAdminRole(req.user as UserProfile);
|
||||
await userService.deleteUser(id);
|
||||
return this.noContent();
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling in Controllers
|
||||
|
||||
```typescript
|
||||
import { NotFoundError, ValidationError, ForbiddenError } from './base.controller';
|
||||
|
||||
// Throw errors - they're handled by the global error handler
|
||||
throw new NotFoundError('Item', id); // 404
|
||||
throw new ValidationError([], 'Invalid'); // 400
|
||||
throw new ForbiddenError('Admin only'); // 403
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```typescript
|
||||
import { Middlewares } from 'tsoa';
|
||||
import { loginLimiter } from '../config/rateLimiters';
|
||||
|
||||
@Post('login')
|
||||
@Middlewares(loginLimiter)
|
||||
@Response<ErrorResponse>(429, 'Too many attempts')
|
||||
public async login(@Body() body: LoginRequest): Promise<...> { ... }
|
||||
```
|
||||
|
||||
### Regenerating Routes
|
||||
|
||||
After modifying controllers, regenerate the tsoa routes:
|
||||
|
||||
```bash
|
||||
npm run tsoa:spec && npm run tsoa:routes
|
||||
```
|
||||
|
||||
**Full Guide**: See [TSOA-MIGRATION-GUIDE.md](./TSOA-MIGRATION-GUIDE.md) for comprehensive documentation.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
**ADR**: [ADR-001](../adr/0001-standardized-error-handling.md)
|
||||
|
||||
Reference in New Issue
Block a user