Massive Dependency Modernization Project
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 3m58s

This commit is contained in:
2026-02-13 00:34:22 -08:00
parent 379b8bf532
commit 2d2cd52011
87 changed files with 26916 additions and 8620 deletions

View File

@@ -2,9 +2,11 @@
**Date**: 2025-12-12
**Status**: Accepted
**Status**: Superseded
**Implemented**: 2026-01-11
**Superseded By**: This ADR was updated in February 2026 to reflect the migration from swagger-jsdoc to tsoa. The original approach using JSDoc annotations has been replaced with a decorator-based controller pattern.
**Implemented**: 2026-02-12
## Context
@@ -16,139 +18,296 @@ Key requirements:
2. **Code-Documentation Sync**: Documentation should stay in sync with the actual code to prevent drift.
3. **Low Maintenance Overhead**: The documentation approach should be "fast and lite" - minimal additional work for developers.
4. **Security**: Documentation should not expose sensitive information in production environments.
5. **Type Safety**: Documentation should be derived from TypeScript types to ensure accuracy.
### Why We Migrated from swagger-jsdoc to tsoa
The original implementation used `swagger-jsdoc` to generate OpenAPI specs from JSDoc comments. This approach had several limitations:
| Issue | Impact |
| --------------------------------------- | -------------------------------------------- |
| `swagger-jsdoc` unmaintained since 2022 | Security and compatibility risks |
| JSDoc duplication with TypeScript types | Maintenance burden, potential for drift |
| No runtime validation from schema | Validation logic separate from documentation |
| Manual type definitions in comments | Error-prone, no compiler verification |
## Decision
We will adopt **OpenAPI 3.0 (Swagger)** for API documentation using the following approach:
We adopt **tsoa** for API documentation using a decorator-based controller pattern:
1. **JSDoc Annotations**: Use `swagger-jsdoc` to generate OpenAPI specs from JSDoc comments in route files.
2. **Swagger UI**: Use `swagger-ui-express` to serve interactive documentation at `/docs/api-docs`.
3. **Environment Restriction**: Only expose the Swagger UI in development and test environments, not production.
4. **Incremental Adoption**: Start with key public routes and progressively add annotations to all endpoints.
1. **Controller Classes**: Use tsoa decorators (`@Route`, `@Get`, `@Post`, `@Security`, etc.) on controller classes.
2. **TypeScript-First**: OpenAPI specs are generated directly from TypeScript interfaces and types.
3. **Swagger UI**: Continue using `swagger-ui-express` to serve interactive documentation at `/docs/api-docs`.
4. **Environment Restriction**: Only expose the Swagger UI in development and test environments, not production.
5. **BaseController Pattern**: All controllers extend a base class providing response formatting utilities.
### Tooling Selection
| Tool | Purpose |
| -------------------- | ---------------------------------------------- |
| `swagger-jsdoc` | Generates OpenAPI 3.0 spec from JSDoc comments |
| `swagger-ui-express` | Serves interactive Swagger UI |
| Tool | Purpose |
| -------------------- | ----------------------------------------------------- |
| `tsoa` (6.6.0) | Generates OpenAPI 3.0 spec from decorators and routes |
| `swagger-ui-express` | Serves interactive Swagger UI |
**Why JSDoc over separate schema files?**
**Why tsoa over swagger-jsdoc?**
- Documentation lives with the code, reducing drift
- No separate files to maintain
- Developers see documentation when editing routes
- Lower learning curve for the team
- **Type-safe contracts**: Decorators derive types directly from TypeScript, eliminating duplicate definitions
- **Active maintenance**: tsoa has an active community and regular releases
- **Route generation**: tsoa generates Express routes automatically, reducing boilerplate
- **Validation integration**: Request body types serve as validation contracts
- **Reduced duplication**: No more parallel JSDoc + TypeScript type definitions
## Implementation Details
### OpenAPI Configuration
### tsoa Configuration
Located in `src/config/swagger.ts`:
Located in `tsoa.json`:
```typescript
import swaggerJsdoc from 'swagger-jsdoc';
const options: swaggerJsdoc.Options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Flyer Crawler API',
version: '1.0.0',
description: 'API for the Flyer Crawler application',
contact: {
name: 'API Support',
},
},
servers: [
{
url: '/api',
description: 'API server',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
```json
{
"entryFile": "server.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*.controller.ts"],
"spec": {
"outputDirectory": "src/config",
"specVersion": 3,
"securityDefinitions": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
},
"basePath": "/api",
"specFileBaseName": "tsoa-spec",
"name": "Flyer Crawler API",
"version": "1.0.0"
},
apis: ['./src/routes/*.ts'],
};
export const swaggerSpec = swaggerJsdoc(options);
"routes": {
"routesDir": "src/routes",
"basePath": "/api",
"middleware": "express",
"routesFileName": "tsoa-generated.ts",
"esm": true,
"authenticationModule": "src/middleware/tsoaAuthentication.ts"
}
}
```
### JSDoc Annotation Pattern
### Controller Pattern
Each route handler should include OpenAPI annotations using the `@openapi` tag:
Each controller extends `BaseController` and uses tsoa decorators:
```typescript
/**
* @openapi
* /health/ping:
* get:
* summary: Simple ping endpoint
* description: Returns a pong response to verify server is responsive
* tags:
* - Health
* responses:
* 200:
* description: Server is responsive
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* properties:
* message:
* type: string
* example: pong
*/
router.get('/ping', validateRequest(emptySchema), (_req: Request, res: Response) => {
return sendSuccess(res, { message: 'pong' });
});
import { Route, Tags, Get, Post, Body, Security, SuccessResponse, Response } from 'tsoa';
import {
BaseController,
SuccessResponse as SuccessResponseType,
ErrorResponse,
} from './base.controller';
interface CreateUserRequest {
email: string;
password: string;
full_name?: string;
}
@Route('users')
@Tags('Users')
export class UserController extends BaseController {
/**
* Create a new user account.
* @summary Create user
* @param requestBody User creation data
* @returns Created user profile
*/
@Post()
@SuccessResponse(201, 'User created')
@Response<ErrorResponse>(400, 'Validation error')
@Response<ErrorResponse>(409, 'Email already exists')
public async createUser(
@Body() requestBody: CreateUserRequest,
): Promise<SuccessResponseType<UserProfileDto>> {
// Implementation
const user = await userService.createUser(requestBody);
return this.created(user);
}
/**
* Get current user's profile.
* @summary Get my profile
* @param request Express request with authenticated user
* @returns User profile
*/
@Get('me')
@Security('bearerAuth')
@SuccessResponse(200, 'Profile retrieved')
@Response<ErrorResponse>(401, 'Not authenticated')
public async getMyProfile(
@Request() request: Express.Request,
): Promise<SuccessResponseType<UserProfileDto>> {
const user = request.user as UserProfile;
return this.success(toUserProfileDto(user));
}
}
```
### Route Documentation Priority
### BaseController Helpers
Document routes in this order of priority:
The `BaseController` class provides standardized response formatting:
1. **Health Routes** - `/api/health/*` (public, critical for operations)
2. **Auth Routes** - `/api/auth/*` (public, essential for integration)
3. **Gamification Routes** - `/api/achievements/*` (simple, good example)
4. **Flyer Routes** - `/api/flyers/*` (core functionality)
5. **User Routes** - `/api/users/*` (common CRUD patterns)
6. **Remaining Routes** - Budget, Recipe, Admin, etc.
```typescript
export abstract class BaseController extends Controller {
// Success response with data
protected success<T>(data: T): SuccessResponse<T> {
return { success: true, data };
}
// Success with 201 Created status
protected created<T>(data: T): SuccessResponse<T> {
this.setStatus(201);
return this.success(data);
}
// Paginated response with metadata
protected paginated<T>(data: T[], pagination: PaginationInput): PaginatedResponse<T> {
return {
success: true,
data,
meta: { pagination: this.calculatePagination(pagination) },
};
}
// Message-only response
protected message(message: string): SuccessResponse<{ message: string }> {
return this.success({ message });
}
// No content response (204)
protected noContent(): void {
this.setStatus(204);
}
// Error response (prefer throwing errors instead)
protected error(code: string, message: string, details?: unknown): ErrorResponse {
return { success: false, error: { code, message, details } };
}
}
```
### Authentication with @Security
tsoa integrates with the existing passport-jwt strategy via a custom authentication module:
```typescript
// src/middleware/tsoaAuthentication.ts
export async function expressAuthentication(
request: Request,
securityName: string,
_scopes?: string[],
): Promise<UserProfile> {
if (securityName !== 'bearerAuth') {
throw new AuthenticationError(`Unknown security scheme: ${securityName}`);
}
const token = extractBearerToken(request);
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
const userProfile = await userRepo.findUserProfileById(decoded.user_id);
if (!userProfile) {
throw new AuthenticationError('User not found');
}
request.user = userProfile;
return userProfile;
}
```
Usage in controllers:
```typescript
@Get('profile')
@Security('bearerAuth')
public async getProfile(@Request() req: Express.Request): Promise<...> {
const user = req.user as UserProfile;
// ...
}
```
### DTO Organization
Shared DTOs are defined in `src/dtos/common.dto.ts` to avoid duplicate type definitions across controllers:
```typescript
// src/dtos/common.dto.ts
/**
* Address with flattened coordinates (tsoa-compatible).
* GeoJSONPoint uses coordinates: [number, number] which tsoa cannot handle.
*/
export interface AddressDto {
address_id: number;
address_line_1: string;
city: string;
province_state: string;
postal_code: string;
country: string;
latitude?: number | null; // Flattened from GeoJSONPoint
longitude?: number | null; // Flattened from GeoJSONPoint
// ...
}
export interface UserDto {
user_id: string;
email: string;
created_at: string;
updated_at: string;
}
export interface UserProfileDto {
full_name?: string | null;
role: 'admin' | 'user';
points: number;
user: UserDto;
address?: AddressDto | null;
// ...
}
```
### Swagger UI Setup
In `server.ts`, add the Swagger UI middleware (development/test only):
In `server.ts`, the Swagger UI middleware serves the tsoa-generated spec:
```typescript
import swaggerUi from 'swagger-ui-express';
import { swaggerSpec } from './src/config/swagger';
import tsoaSpec from './src/config/tsoa-spec.json' with { type: 'json' };
// Only serve Swagger UI in non-production environments
if (process.env.NODE_ENV !== 'production') {
app.use('/docs/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.use('/docs/api-docs', swaggerUi.serve, swaggerUi.setup(tsoaSpec));
// Optionally expose raw JSON spec for tooling
// Raw JSON spec for tooling
app.get('/docs/api-docs.json', (_req, res) => {
res.setHeader('Content-Type', 'application/json');
res.send(swaggerSpec);
res.send(tsoaSpec);
});
}
```
### Build Integration
tsoa spec and route generation is integrated into the build pipeline:
```json
{
"scripts": {
"tsoa:spec": "tsoa spec",
"tsoa:routes": "tsoa routes",
"prebuild": "npm run tsoa:spec && npm run tsoa:routes",
"build": "tsc"
}
}
```
### Response Schema Standardization
All API responses follow the standardized format from [ADR-028](./0028-api-response-standardization.md):
@@ -160,107 +319,144 @@ All API responses follow the standardized format from [ADR-028](./0028-api-respo
"data": { ... }
}
// Paginated response
{
"success": true,
"data": [...],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5,
"hasNextPage": true,
"hasPrevPage": false
}
}
}
// Error response
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
"code": "NOT_FOUND",
"message": "User not found"
}
}
```
Define reusable schema components for these patterns:
```typescript
/**
* @openapi
* components:
* schemas:
* SuccessResponse:
* type: object
* properties:
* success:
* type: boolean
* example: true
* data:
* type: object
* ErrorResponse:
* type: object
* properties:
* success:
* type: boolean
* example: false
* error:
* type: object
* properties:
* code:
* type: string
* message:
* type: string
*/
```
### Security Considerations
1. **Production Disabled**: Swagger UI is not available in production to prevent information disclosure.
2. **No Sensitive Data**: Never include actual secrets, tokens, or PII in example values.
3. **Authentication Documented**: Clearly document which endpoints require authentication.
## API Route Tags
Organize endpoints using consistent tags:
| Tag | Description | Routes |
| Tag | Description | Route Prefix |
| ------------ | ---------------------------------- | --------------------- |
| Health | Server health and readiness checks | `/api/health/*` |
| Auth | Authentication and authorization | `/api/auth/*` |
| Users | User profile management | `/api/users/*` |
| Flyers | Flyer uploads and retrieval | `/api/flyers/*` |
| Achievements | Gamification and leaderboards | `/api/achievements/*` |
| Budgets | Budget tracking | `/api/budgets/*` |
| Deals | Deal search and management | `/api/deals/*` |
| Stores | Store information | `/api/stores/*` |
| Recipes | Recipe management | `/api/recipes/*` |
| Budgets | Budget tracking | `/api/budgets/*` |
| Inventory | User inventory management | `/api/inventory/*` |
| Gamification | Achievements and leaderboards | `/api/achievements/*` |
| Admin | Administrative operations | `/api/admin/*` |
| System | System status and monitoring | `/api/system/*` |
## Controller Inventory
The following controllers have been migrated to tsoa:
| Controller | Endpoints | Description |
| ------------------------------- | --------- | ----------------------------------------- |
| `health.controller.ts` | 10 | Health checks, probes, service status |
| `auth.controller.ts` | 8 | Login, register, password reset, OAuth |
| `user.controller.ts` | 30 | User profiles, preferences, notifications |
| `admin.controller.ts` | 32 | System administration, user management |
| `ai.controller.ts` | 15 | AI-powered extraction and analysis |
| `flyer.controller.ts` | 12 | Flyer upload and management |
| `store.controller.ts` | 8 | Store information |
| `recipe.controller.ts` | 10 | Recipe CRUD and suggestions |
| `upc.controller.ts` | 6 | UPC barcode lookups |
| `inventory.controller.ts` | 8 | User inventory management |
| `receipt.controller.ts` | 6 | Receipt processing |
| `budget.controller.ts` | 8 | Budget tracking |
| `category.controller.ts` | 4 | Category management |
| `deals.controller.ts` | 8 | Deal search and discovery |
| `stats.controller.ts` | 6 | Usage statistics |
| `price.controller.ts` | 6 | Price history and tracking |
| `system.controller.ts` | 4 | System status |
| `gamification.controller.ts` | 10 | Achievements, leaderboards |
| `personalization.controller.ts` | 6 | User recommendations |
| `reactions.controller.ts` | 4 | Item reactions and ratings |
## Security Considerations
1. **Production Disabled**: Swagger UI is not available in production to prevent information disclosure.
2. **No Sensitive Data**: Never include actual secrets, tokens, or PII in example values.
3. **Authentication Documented**: Clearly document which endpoints require authentication.
4. **Rate Limiting**: Rate limiters are applied via `@Middlewares` decorator.
## Testing
Verify API documentation is correct by:
1. **Manual Review**: Navigate to `/docs/api-docs` and test each endpoint.
2. **Spec Validation**: Use OpenAPI validators to check the generated spec.
3. **Integration Tests**: Existing integration tests serve as implicit documentation verification.
3. **Controller Tests**: Each controller has comprehensive test coverage (369 controller tests total).
4. **Integration Tests**: 345 integration tests verify endpoint behavior.
## Consequences
### Positive
- **Single Source of Truth**: Documentation lives with the code and stays in sync.
- **Interactive Exploration**: Developers can try endpoints directly from the UI.
- **SDK Generation**: OpenAPI spec enables automatic client SDK generation.
- **Onboarding**: New developers can quickly understand the API surface.
- **Low Overhead**: JSDoc annotations are minimal additions to existing code.
- **Type-safe API contracts**: tsoa decorators derive types from TypeScript, eliminating duplicate definitions
- **Single Source of Truth**: Documentation lives with the code and stays in sync
- **Active Maintenance**: tsoa is actively maintained with regular releases
- **Interactive Exploration**: Developers can try endpoints directly from Swagger UI
- **SDK Generation**: OpenAPI spec enables automatic client SDK generation
- **Reduced Boilerplate**: tsoa generates Express routes automatically
### Negative
- **Maintenance Required**: Developers must update annotations when routes change.
- **Build Dependency**: Adds `swagger-jsdoc` and `swagger-ui-express` packages.
- **Initial Investment**: Existing routes need annotations added incrementally.
- **Learning Curve**: Decorator-based controller pattern differs from Express handlers
- **Generated Code**: `tsoa-generated.ts` must be regenerated when controllers change
- **Build Step**: Adds `tsoa spec && tsoa routes` to the build pipeline
### Mitigation
- Include documentation checks in code review process.
- Start with high-priority routes and expand coverage over time.
- Use TypeScript types to reduce documentation duplication where possible.
- **Migration Guide**: Created comprehensive TSOA-MIGRATION-GUIDE.md for developers
- **BaseController**: Provides familiar response helpers matching existing patterns
- **Incremental Adoption**: Existing Express routes continue to work alongside tsoa controllers
## Key Files
- `src/config/swagger.ts` - OpenAPI configuration
- `src/routes/*.ts` - Route files with JSDoc annotations
- `server.ts` - Swagger UI middleware setup
| File | Purpose |
| -------------------------------------- | --------------------------------------- |
| `tsoa.json` | tsoa configuration |
| `src/controllers/base.controller.ts` | Base controller with response utilities |
| `src/controllers/types.ts` | Shared controller type definitions |
| `src/controllers/*.controller.ts` | Individual domain controllers |
| `src/dtos/common.dto.ts` | Shared DTO definitions |
| `src/middleware/tsoaAuthentication.ts` | JWT authentication handler |
| `src/routes/tsoa-generated.ts` | tsoa-generated Express routes |
| `src/config/tsoa-spec.json` | Generated OpenAPI 3.0 spec |
| `server.ts` | Swagger UI middleware setup |
## Migration History
| Date | Change |
| ---------- | --------------------------------------------------------------- |
| 2025-12-12 | Initial ADR created with swagger-jsdoc approach |
| 2026-01-11 | Began implementation with swagger-jsdoc |
| 2026-02-12 | Completed migration to tsoa, superseding swagger-jsdoc approach |
## Related ADRs
- [ADR-059](./0059-dependency-modernization.md) - Dependency Modernization (tsoa migration plan)
- [ADR-003](./0003-standardized-input-validation-using-middleware.md) - Input Validation (Zod schemas)
- [ADR-028](./0028-api-response-standardization.md) - Response Standardization
- [ADR-001](./0001-standardized-error-handling.md) - Error Handling
- [ADR-016](./0016-api-security-hardening.md) - Security Hardening
- [ADR-048](./0048-authentication-strategy.md) - Authentication Strategy

View File

@@ -0,0 +1,308 @@
# ADR-059: Dependency Modernization Plan
**Status**: Accepted
**Date**: 2026-02-12
**Implemented**: 2026-02-12
## Context
NPM audit and security scanning identified deprecated dependencies requiring modernization:
| Dependency | Current | Issue | Replacement |
| --------------- | ------- | ----------------------- | --------------------------------------- |
| `swagger-jsdoc` | 6.2.8 | Unmaintained since 2022 | `tsoa` (decorator-based OpenAPI) |
| `rimraf` | 6.1.2 | Legacy cleanup utility | Node.js `fs.rm()` (native since v14.14) |
**Constraints**:
- Existing `@openapi` JSDoc annotations in 20 route files
- ADR-018 compliance (API documentation strategy)
- Zero-downtime migration (phased approach)
- Must maintain Express 5.x compatibility
## Decision
### 1. swagger-jsdoc → tsoa Migration
**Architecture**: tsoa controller classes + Express integration (no replacement of Express routing layer).
```text
Current: Route Files → JSDoc Annotations → swagger-jsdoc → OpenAPI Spec
Future: Controller Classes → @Route/@Get decorators → tsoa → OpenAPI Spec + Route Registration
```
**Controller Pattern**: Base controller providing common utilities:
```typescript
// src/controllers/base.controller.ts
export abstract class BaseController {
protected sendSuccess<T>(res: Response, data: T, status = 200) {
return sendSuccess(res, data, status);
}
protected sendError(
res: Response,
code: ErrorCode,
msg: string,
status: number,
details?: unknown,
) {
return sendError(res, code, msg, status, details);
}
}
```
**Express Integration Strategy**: tsoa generates routes.ts; wrap with Express middleware pipeline:
```typescript
// server.ts integration
import { RegisterRoutes } from './src/generated/routes';
RegisterRoutes(app); // tsoa registers routes with existing Express app
```
### 2. rimraf → fs.rm() Migration
**Change**: Replace `rimraf coverage .coverage` script with Node.js native API.
```json
// package.json (before)
"clean": "rimraf coverage .coverage"
// package.json (after)
"clean": "node -e \"import('fs/promises').then(fs => Promise.all([fs.rm('coverage', {recursive:true,force:true}), fs.rm('.coverage', {recursive:true,force:true})]))\""
```
**Alternative**: Create `scripts/clean.mjs` for maintainability:
```javascript
// scripts/clean.mjs
import { rm } from 'fs/promises';
await Promise.all([
rm('coverage', { recursive: true, force: true }),
rm('.coverage', { recursive: true, force: true }),
]);
```
## Implementation Plan
### Phase 1: Infrastructure (Tasks 1-4)
| Task | Description | Dependencies |
| ---- | ---------------------------------------------- | ------------ |
| 1 | Install tsoa, configure tsoa.json | None |
| 2 | Create BaseController with utility methods | Task 1 |
| 3 | Configure Express integration (RegisterRoutes) | Task 2 |
| 4 | Set up tsoa spec generation in build pipeline | Task 3 |
### Phase 2: Controller Migration (Tasks 5-14)
Priority order matches ADR-018:
| Task | Route File | Controller Class | Dependencies |
| ---- | ----------------------------------------------------------------------------------------------------------------- | ---------------------- | ------------ |
| 5 | health.routes.ts | HealthController | Task 4 |
| 6 | auth.routes.ts | AuthController | Task 4 |
| 7 | gamification.routes.ts | AchievementsController | Task 4 |
| 8 | flyer.routes.ts | FlyersController | Task 4 |
| 9 | user.routes.ts | UsersController | Task 4 |
| 10 | budget.routes.ts | BudgetController | Task 4 |
| 11 | recipe.routes.ts | RecipeController | Task 4 |
| 12 | store.routes.ts | StoreController | Task 4 |
| 13 | admin.routes.ts | AdminController | Task 4 |
| 14 | Remaining routes (deals, price, upc, inventory, ai, receipt, category, stats, personalization, reactions, system) | Various | Task 4 |
### Phase 3: Cleanup and rimraf (Tasks 15-18)
| Task | Description | Dependencies |
| ---- | -------------------------------- | ------------------- |
| 15 | Create scripts/clean.mjs | None |
| 16 | Update package.json clean script | Task 15 |
| 17 | Remove rimraf dependency | Task 16 |
| 18 | Remove swagger-jsdoc + types | Tasks 5-14 complete |
### Phase 4: Verification (Tasks 19-24)
| Task | Description | Dependencies |
| ---- | --------------------------------- | ------------ |
| 19 | Run type-check | Tasks 15-18 |
| 20 | Run unit tests | Task 19 |
| 21 | Run integration tests | Task 20 |
| 22 | Verify OpenAPI spec completeness | Task 21 |
| 23 | Update ADR-018 (reference tsoa) | Task 22 |
| 24 | Update CLAUDE.md (swagger → tsoa) | Task 23 |
### Task Dependency Graph
```text
[1: Install tsoa]
|
[2: BaseController]
|
[3: Express Integration]
|
[4: Build Pipeline]
|
+------------------+------------------+
| | | | |
[5] [6] [7] [8] [9-14]
Health Auth Gamif Flyer Others
| | | | |
+------------------+------------------+
|
[18: Remove swagger-jsdoc]
|
[15: clean.mjs] -----> [16: Update pkg.json]
|
[17: Remove rimraf]
|
[19: type-check]
|
[20: unit tests]
|
[21: integration tests]
|
[22: Verify OpenAPI]
|
[23: Update ADR-018]
|
[24: Update CLAUDE.md]
```
### Critical Path
**Minimum time to completion**: Tasks 1 → 2 → 3 → 4 → 5 (or any controller) → 18 → 19 → 20 → 21 → 22 → 23 → 24
**Parallelization opportunities**:
- Tasks 5-14 (all controller migrations) can run in parallel after Task 4
- Tasks 15-17 (rimraf removal) can run in parallel with controller migrations
## Technical Decisions
### tsoa Configuration
```json
// tsoa.json
{
"entryFile": "server.ts",
"noImplicitAdditionalProperties": "throw-on-extras",
"controllerPathGlobs": ["src/controllers/**/*.controller.ts"],
"spec": {
"outputDirectory": "src/generated",
"specVersion": 3,
"basePath": "/api/v1"
},
"routes": {
"routesDir": "src/generated",
"middleware": "express"
}
}
```
### Decorator Migration Example
**Before** (swagger-jsdoc):
```typescript
/**
* @openapi
* /health/ping:
* get:
* summary: Simple ping endpoint
* tags: [Health]
* responses:
* 200:
* description: Server is responsive
*/
router.get('/ping', validateRequest(emptySchema), handler);
```
**After** (tsoa):
```typescript
@Route('health')
@Tags('Health')
export class HealthController extends BaseController {
@Get('ping')
@SuccessResponse(200, 'Server is responsive')
public async ping(): Promise<{ message: string }> {
return { message: 'pong' };
}
}
```
### Zod Integration
tsoa uses its own validation. Options:
1. **Replace Zod with tsoa validation** - Use `@Body`, `@Query`, `@Path` decorators with TypeScript types
2. **Hybrid approach** - Keep Zod schemas, call `validateRequest()` within controller methods
3. **Custom template** - Generate tsoa routes that call Zod validation middleware
**Recommended**: Option 1 for new controllers; gradually migrate existing Zod schemas.
## Risk Mitigation
| Risk | Likelihood | Impact | Mitigation |
| --------------------------------------- | ---------- | ------ | ------------------------------------------- |
| tsoa/Express 5.x incompatibility | Medium | High | Test in dev container before migration |
| Missing OpenAPI coverage post-migration | Low | Medium | Compare generated specs before/after |
| Authentication middleware integration | Medium | Medium | Test @Security decorator with passport-jwt |
| Test regression from route changes | Low | High | Run full test suite after each controller |
| Build time increase (tsoa generation) | Low | Low | Add to npm run build; cache generated files |
## Consequences
### Positive
- **Type-safe API contracts**: tsoa decorators derive types from TypeScript
- **Reduced duplication**: No more parallel JSDoc + TypeScript type definitions
- **Modern tooling**: Active tsoa community (vs. unmaintained swagger-jsdoc)
- **Native Node.js**: fs.rm() is built-in, no external dependency
- **Smaller dependency tree**: Remove rimraf (5 transitive deps) + swagger-jsdoc (8 transitive deps)
### Negative
- **Learning curve**: Decorator-based controller pattern differs from Express handlers
- **Migration effort**: 20 route files require conversion
- **Generated code**: `src/generated/routes.ts` must be version-controlled or regenerated on build
### Neutral
- **Build step change**: Add `tsoa spec && tsoa routes` to build pipeline
- **Testing approach**: May need to adjust test structure for controller classes
## Alternatives Considered
### 1. Update swagger-jsdoc to fork/successor
**Rejected**: No active fork; community has moved to tsoa, fastify-swagger, or NestJS.
### 2. NestJS migration
**Rejected**: Full framework migration (Express → NestJS) is disproportionate to the problem scope.
### 3. fastify-swagger
**Rejected**: Requires Express → Fastify migration; out of scope.
### 4. Keep rimraf, accept deprecation warning
**Rejected**: Native fs.rm() is trivial replacement; no reason to maintain deprecated dependency.
## Key Files
| File | Purpose |
| ------------------------------------ | ------------------------------------- |
| `tsoa.json` | tsoa configuration |
| `src/controllers/base.controller.ts` | Base controller with utilities |
| `src/controllers/*.controller.ts` | Individual domain controllers |
| `src/generated/routes.ts` | tsoa-generated Express routes |
| `src/generated/swagger.json` | Generated OpenAPI 3.0 spec |
| `scripts/clean.mjs` | Native fs.rm() replacement for rimraf |
## Related ADRs
- [ADR-018](./0018-api-documentation-strategy.md) - API Documentation Strategy (will be updated)
- [ADR-003](./0003-standardized-input-validation-using-middleware.md) - Input Validation (Zod integration)
- [ADR-028](./0028-api-response-standardization.md) - Response Standardization (BaseController pattern)
- [ADR-001](./0001-standardized-error-handling.md) - Error Handling (error utilities in BaseController)

View File

@@ -23,7 +23,7 @@ This directory contains a log of the architectural decisions made for the Flyer
**[ADR-003](./0003-standardized-input-validation-using-middleware.md)**: Standardized Input Validation using Middleware (Accepted)
**[ADR-008](./0008-api-versioning-strategy.md)**: API Versioning Strategy (Accepted - Phase 2 Complete)
**[ADR-018](./0018-api-documentation-strategy.md)**: API Documentation Strategy (Accepted)
**[ADR-018](./0018-api-documentation-strategy.md)**: API Documentation Strategy (Superseded - tsoa migration complete)
**[ADR-022](./0022-real-time-notification-system.md)**: Real-time Notification System (Accepted)
**[ADR-028](./0028-api-response-standardization.md)**: API Response Standardization and Envelope Pattern (Implemented)
@@ -74,6 +74,7 @@ This directory contains a log of the architectural decisions made for the Flyer
**[ADR-045](./0045-test-data-factories-and-fixtures.md)**: Test Data Factories and Fixtures (Accepted)
**[ADR-047](./0047-project-file-and-folder-organization.md)**: Project File and Folder Organization (Proposed)
**[ADR-057](./0057-test-remediation-post-api-versioning.md)**: Test Remediation Post-API Versioning (Accepted)
**[ADR-059](./0059-dependency-modernization.md)**: Dependency Modernization - tsoa Migration (Accepted)
## 9. Architecture Patterns