This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

The updated `shared_types.md` provides a comprehensive guide for implementing npm workspaces to share types between the frontend and backend. The key changes include:

1. More detailed explanation of why npm workspaces are chosen
2. Expanded implementation plan with step-by-step instructions
3. Detailed configuration for `package.json` and `tsconfig.json`
4. Comprehensive migration checklist
5. More in-depth troubleshooting and best practices sections

The document now provides a much clearer roadmap for:
- Setting up workspace infrastructure
- Migrating existing types
- Updating frontend and backend imports
- Managing development workflow
- Handling potential issues

Would you like me to elaborate on any specific section or help you start implementing the migration?

Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) <aider@aider.chat>

+291 -162
+291 -162
docs/shared_types.md
··· 1 - # Shared Types Architecture 1 + # Shared Types Architecture - npm Workspaces Implementation 2 2 3 - This document outlines the recommended approach for sharing types between the backend (`src/`) and webapp (`src/webapp/`) in our monorepo structure. 3 + This document outlines the implementation plan for sharing types between the backend (`src/`) and webapp (`src/webapp/`) using npm workspaces in our monorepo structure. 4 4 5 5 ## Overview 6 6 7 - We use a shared API types directory that both backend and frontend import from, ensuring type safety and consistency across the entire application. 7 + We use npm workspaces to create a shared types package that both backend and frontend import from as a proper npm dependency, ensuring type safety and consistency across the entire application. 8 + 9 + ## Architecture Decision: npm Workspaces 10 + 11 + We chose npm workspaces over simpler approaches because: 12 + 13 + - ✅ **Industry standard** - Professional monorepo structure 14 + - ✅ **Proper dependency management** - npm handles versioning and dependencies 15 + - ✅ **Scalability** - Easy to add more packages (mobile app, CLI tools, etc.) 16 + - ✅ **Build isolation** - Each package has its own build process 17 + - ✅ **Publishing ready** - Can publish shared types as separate npm package 18 + - ✅ **IDE support** - Better IntelliSense and go-to-definition 19 + - ✅ **Version management** - Can version shared types independently 8 20 9 - ## Directory Structure 21 + ## Final Directory Structure 10 22 11 23 ``` 12 - src/ 13 - ├── shared/ 14 - │ └── api/ 15 - │ ├── requests.ts # Request types for all API endpoints 16 - │ ├── responses.ts # Response types for all API endpoints 17 - │ ├── common.ts # Common types (User, Pagination, etc.) 18 - │ └── index.ts # Re-exports all types 19 - ├── modules/ # Backend modules 20 - └── webapp/ # Frontend application 24 + annos/ 25 + ├── package.json # Workspace root 26 + ├── src/ 27 + │ ├── shared/ 28 + │ │ ├── package.json # @annos/shared-types package 29 + │ │ ├── tsconfig.json 30 + │ │ ├── src/ 31 + │ │ │ ├── api/ 32 + │ │ │ │ ├── index.ts # Re-exports all types 33 + │ │ │ │ ├── common.ts # Common types (User, Pagination, etc.) 34 + │ │ │ │ ├── requests.ts # Request types for all API endpoints 35 + │ │ │ │ └── responses.ts # Response types for all API endpoints 36 + │ │ │ └── index.ts # Main entry point 37 + │ │ └── dist/ # Compiled output 38 + │ ├── modules/ # Backend modules 39 + │ └── webapp/ 40 + │ ├── package.json # @annos/webapp package 41 + │ └── ... 21 42 ``` 22 43 23 - ## Benefits 44 + ## Implementation Plan 24 45 25 - - ✅ **Zero additional tooling** - just TypeScript imports 26 - - ✅ **Compile-time safety** - type mismatches caught immediately 27 - - ✅ **Single source of truth** - one place to update API contracts 28 - - ✅ **Easy refactoring** - TypeScript will catch all references 29 - - ✅ **No build complexity** - works with existing setup 30 - - ✅ **Monorepo friendly** - leverages existing structure 46 + ### Phase 1: Setup Workspace Infrastructure 47 + 48 + #### Step 1.1: Configure Root Workspace 49 + 50 + Update root `package.json`: 51 + 52 + ```json 53 + { 54 + "name": "annos", 55 + "version": "1.0.0", 56 + "workspaces": [ 57 + "src/shared", 58 + "src/webapp", 59 + "." 60 + ], 61 + "scripts": { 62 + "build:shared": "npm run build --workspace=@annos/shared-types", 63 + "dev:shared": "npm run dev --workspace=@annos/shared-types", 64 + "build:webapp": "npm run build --workspace=@annos/webapp", 65 + "dev:webapp": "npm run dev --workspace=@annos/webapp", 66 + "dev:all": "npm run dev:shared & npm run dev:webapp & npm run dev:app:inner" 67 + } 68 + } 69 + ``` 70 + 71 + #### Step 1.2: Create Shared Types Package 72 + 73 + Create `src/shared/package.json`: 74 + 75 + ```json 76 + { 77 + "name": "@annos/shared-types", 78 + "version": "1.0.0", 79 + "description": "Shared TypeScript types for Annos API", 80 + "main": "dist/index.js", 81 + "types": "dist/index.d.ts", 82 + "files": [ 83 + "dist/**/*" 84 + ], 85 + "scripts": { 86 + "build": "tsc", 87 + "dev": "tsc --watch", 88 + "clean": "rm -rf dist" 89 + }, 90 + "devDependencies": { 91 + "typescript": "^5.8.3" 92 + } 93 + } 94 + ``` 95 + 96 + Create `src/shared/tsconfig.json`: 97 + 98 + ```json 99 + { 100 + "compilerOptions": { 101 + "target": "ES2020", 102 + "module": "CommonJS", 103 + "lib": ["ES2020"], 104 + "outDir": "./dist", 105 + "rootDir": "./src", 106 + "strict": true, 107 + "esModuleInterop": true, 108 + "skipLibCheck": true, 109 + "forceConsistentCasingInFileNames": true, 110 + "declaration": true, 111 + "declarationMap": true, 112 + "sourceMap": true 113 + }, 114 + "include": ["src/**/*"], 115 + "exclude": ["node_modules", "dist"] 116 + } 117 + ``` 31 118 32 - ## Implementation Guide 119 + #### Step 1.3: Update Webapp Package 33 120 34 - ### 1. Create Shared Types Directory 121 + Update `src/webapp/package.json`: 35 122 36 - ```bash 37 - mkdir -p src/shared/api 123 + ```json 124 + { 125 + "name": "@annos/webapp", 126 + "dependencies": { 127 + "@annos/shared-types": "workspace:*", 128 + // ... existing dependencies 129 + } 130 + } 38 131 ``` 39 132 40 - ### 2. Define Common Types 133 + ### Phase 2: Migrate Types to Shared Package 134 + 135 + #### Step 2.1: Create Shared Type Files 41 136 137 + Move and organize existing webapp types into the shared package: 138 + 139 + **src/shared/src/api/common.ts:** 42 140 ```typescript 43 - // src/shared/api/common.ts 44 141 export interface User { 45 142 id: string; 46 143 name: string; ··· 68 165 export interface CollectionSorting extends BaseSorting { 69 166 sortBy: 'name' | 'createdAt' | 'updatedAt' | 'cardCount'; 70 167 } 71 - ``` 72 168 73 - ### 3. Define Request Types 169 + export interface FeedPagination extends Pagination { 170 + nextCursor?: string; 171 + } 172 + ``` 74 173 174 + **src/shared/src/api/requests.ts:** 75 175 ```typescript 76 - // src/shared/api/requests.ts 176 + // Copy all request types from src/webapp/api-client/types/requests.ts 77 177 export interface PaginationParams { 78 178 page?: number; 79 179 limit?: number; ··· 84 184 sortOrder?: 'asc' | 'desc'; 85 185 } 86 186 87 - export interface GetMyUrlCardsRequest extends PaginationParams, SortingParams {} 88 - 89 - export interface AddUrlToLibraryRequest { 90 - url: string; 91 - note?: string; 92 - collectionIds?: string[]; 93 - } 94 - 95 - // ... other request types 187 + // ... all other request types 96 188 ``` 97 189 98 - ### 4. Define Response Types 99 - 190 + **src/shared/src/api/responses.ts:** 100 191 ```typescript 101 - // src/shared/api/responses.ts 102 - import { User, Pagination, CardSorting } from './common'; 192 + import { User, Pagination, CardSorting, CollectionSorting, FeedPagination } from './common'; 103 193 194 + // Copy all response types from src/webapp/api-client/types/responses.ts 104 195 export interface UrlCard { 105 196 id: string; 106 197 type: 'URL'; 107 198 url: string; 108 - cardContent: { 109 - url: string; 110 - title?: string; 111 - description?: string; 112 - author?: string; 113 - thumbnailUrl?: string; 114 - }; 115 - libraryCount: number; 116 - urlLibraryCount: number; 117 - urlInLibrary?: boolean; 118 - createdAt: string; 119 - updatedAt: string; 120 - author: User; 121 - note?: { 122 - id: string; 123 - text: string; 124 - }; 125 - } 126 - 127 - export interface GetUrlCardsResponse { 128 - cards: UrlCard[]; 129 - pagination: Pagination; 130 - sorting: CardSorting; 199 + // ... rest of UrlCard interface 131 200 } 132 201 133 - // ... other response types 202 + // ... all other response types 134 203 ``` 135 204 136 - ### 5. Create Index File 137 - 205 + **src/shared/src/api/index.ts:** 138 206 ```typescript 139 - // src/shared/api/index.ts 140 207 export * from './common'; 141 208 export * from './requests'; 142 209 export * from './responses'; 143 210 ``` 144 211 145 - ### 6. Update Backend Use Cases 212 + **src/shared/src/index.ts:** 213 + ```typescript 214 + export * from './api'; 215 + ``` 216 + 217 + #### Step 2.2: Build Shared Types 218 + 219 + ```bash 220 + cd src/shared 221 + npm run build 222 + ``` 223 + 224 + ### Phase 3: Update Frontend to Use Shared Types 225 + 226 + #### Step 3.1: Install Shared Types Dependency 227 + 228 + ```bash 229 + npm install --workspace=@annos/webapp 230 + ``` 231 + 232 + #### Step 3.2: Update Frontend Imports 233 + 234 + Replace all imports in webapp files: 235 + 236 + ```typescript 237 + // OLD: src/webapp/api-client/ApiClient.ts 238 + import type { 239 + GetUrlCardsResponse, 240 + AddUrlToLibraryRequest, 241 + } from './types/responses'; 242 + 243 + // NEW: 244 + import type { 245 + GetUrlCardsResponse, 246 + AddUrlToLibraryRequest, 247 + } from '@annos/shared-types'; 248 + ``` 249 + 250 + #### Step 3.3: Remove Old Type Files 251 + 252 + ```bash 253 + rm -rf src/webapp/api-client/types/ 254 + ``` 255 + 256 + ### Phase 4: Update Backend to Use Shared Types 257 + 258 + #### Step 4.1: Install Shared Types in Backend 259 + 260 + Add to root `package.json` dependencies: 261 + 262 + ```json 263 + { 264 + "dependencies": { 265 + "@annos/shared-types": "workspace:*" 266 + } 267 + } 268 + ``` 269 + 270 + #### Step 4.2: Update Use Cases 146 271 147 272 ```typescript 148 273 // src/modules/cards/application/useCases/queries/GetUrlCardsUseCase.ts 149 - import { GetUrlCardsResponse } from '../../../../shared/api'; 274 + import { GetUrlCardsResponse } from '@annos/shared-types'; 150 275 151 276 export class GetUrlCardsUseCase { 152 277 async execute( 153 - query: GetUrlCardsQuery, 154 - ): Promise< 155 - Result<GetUrlCardsResponse, ValidationError | AppError.UnexpectedError> 156 - > { 157 - // Implementation returns GetUrlCardsResponse type 278 + query: GetUrlCardsQuery 279 + ): Promise<Result<GetUrlCardsResponse, ValidationError | AppError.UnexpectedError>> { 280 + // Implementation must return GetUrlCardsResponse type 158 281 return ok({ 159 282 cards: enrichedCards, 160 283 pagination: { ··· 173 296 } 174 297 ``` 175 298 176 - ### 7. Update Controllers 299 + #### Step 4.3: Update Controllers 177 300 178 301 ```typescript 179 302 // src/modules/cards/infrastructure/http/controllers/GetMyUrlCardsController.ts 180 - import { GetUrlCardsResponse } from '../../../../shared/api'; 303 + import { GetUrlCardsResponse } from '@annos/shared-types'; 181 304 182 305 export class GetMyUrlCardsController extends Controller { 183 306 async executeImpl(req: AuthenticatedRequest, res: Response): Promise<any> { ··· 193 316 } 194 317 ``` 195 318 196 - ### 8. Update Frontend API Client 319 + ### Phase 5: Development Workflow 197 320 198 - ```typescript 199 - // src/webapp/api-client/ApiClient.ts 200 - import type { 201 - GetUrlCardsResponse, 202 - GetMyUrlCardsRequest, 203 - AddUrlToLibraryRequest, 204 - AddUrlToLibraryResponse, 205 - // ... other types 206 - } from '../../shared/api'; 321 + #### Step 5.1: Development Scripts 207 322 208 - export class ApiClient { 209 - async getMyUrlCards( 210 - params?: GetMyUrlCardsRequest, 211 - ): Promise<GetUrlCardsResponse> { 212 - return this.queryClient.getMyUrlCards(params); 213 - } 323 + Add to root `package.json`: 214 324 215 - async addUrlToLibrary( 216 - request: AddUrlToLibraryRequest, 217 - ): Promise<AddUrlToLibraryResponse> { 218 - return this.cardClient.addUrlToLibrary(request); 325 + ```json 326 + { 327 + "scripts": { 328 + "dev": "concurrently \"npm run dev:shared\" \"npm run dev:webapp\" \"npm run dev:app:inner\"", 329 + "dev:shared": "npm run dev --workspace=@annos/shared-types", 330 + "build:all": "npm run build:shared && npm run build:webapp && npm run build" 219 331 } 220 332 } 221 333 ``` 222 334 223 - ## Migration Strategy 224 - 225 - ### Phase 1: Setup Infrastructure 335 + #### Step 5.2: Type Development Workflow 226 336 227 - 1. Create `src/shared/api/` directory structure 228 - 2. Move existing webapp types to shared location 229 - 3. Update webapp imports to use shared types 337 + 1. **Make type changes** in `src/shared/src/api/` 338 + 2. **Shared types auto-rebuild** (if using `npm run dev:shared`) 339 + 3. **Both frontend and backend** get updated types automatically 340 + 4. **TypeScript compiler** catches any mismatches immediately 230 341 231 - ### Phase 2: Backend Integration (Per Endpoint) 342 + ### Phase 6: Testing and Validation 232 343 233 - For each API endpoint: 344 + #### Step 6.1: Type Safety Validation 234 345 235 - 1. **Identify the endpoint** (e.g., `GET /api/cards/my`) 236 - 2. **Update use case** to return shared response type 237 - 3. **Update controller** to use shared types 238 - 4. **Test the endpoint** to ensure types match 346 + ```bash 347 + # Check all TypeScript compilation 348 + npm run type-check 349 + npm run type-check --workspace=@annos/webapp 350 + npm run build:shared 351 + ``` 239 352 240 - ### Phase 3: Validation (Optional) 353 + #### Step 6.2: Runtime Validation (Optional) 241 354 242 - Add runtime validation using libraries like Zod: 355 + Add Zod schemas for runtime validation: 243 356 357 + **src/shared/src/validation/index.ts:** 244 358 ```typescript 245 - // src/shared/api/validation.ts 246 359 import { z } from 'zod'; 247 360 248 361 export const UrlCardSchema = z.object({ ··· 254 367 255 368 export const GetUrlCardsResponseSchema = z.object({ 256 369 cards: z.array(UrlCardSchema), 257 - pagination: PaginationSchema, 258 - sorting: CardSortingSchema, 370 + pagination: z.object({ 371 + currentPage: z.number(), 372 + totalPages: z.number(), 373 + totalCount: z.number(), 374 + hasMore: z.boolean(), 375 + limit: z.number(), 376 + }), 377 + sorting: z.object({ 378 + sortBy: z.enum(['createdAt', 'updatedAt', 'libraryCount']), 379 + sortOrder: z.enum(['asc', 'desc']), 380 + }), 259 381 }); 260 382 ``` 261 383 262 - ## Example: Complete Flow 384 + ## Migration Checklist 263 385 264 - Here's how a complete request/response flow works: 386 + ### Phase 1: Infrastructure ✅ 387 + - [ ] Update root `package.json` with workspaces 388 + - [ ] Create `src/shared/package.json` 389 + - [ ] Create `src/shared/tsconfig.json` 390 + - [ ] Update `src/webapp/package.json` dependencies 391 + - [ ] Run `npm install` to setup workspace 265 392 266 - 1. **Frontend makes request:** 393 + ### Phase 2: Type Migration ✅ 394 + - [ ] Create `src/shared/src/api/common.ts` 395 + - [ ] Create `src/shared/src/api/requests.ts` 396 + - [ ] Create `src/shared/src/api/responses.ts` 397 + - [ ] Create `src/shared/src/api/index.ts` 398 + - [ ] Create `src/shared/src/index.ts` 399 + - [ ] Build shared types: `npm run build:shared` 267 400 268 - ```typescript 269 - const response: GetUrlCardsResponse = await apiClient.getMyUrlCards({ 270 - page: 1, 271 - limit: 20, 272 - sortBy: 'updatedAt', 273 - sortOrder: 'desc', 274 - }); 275 - ``` 401 + ### Phase 3: Frontend Migration ✅ 402 + - [ ] Update all imports in webapp to use `@annos/shared-types` 403 + - [ ] Remove old type files: `rm -rf src/webapp/api-client/types/` 404 + - [ ] Test webapp compilation: `npm run type-check --workspace=@annos/webapp` 276 405 277 - 2. **Controller receives request:** 406 + ### Phase 4: Backend Migration ✅ 407 + - [ ] Add shared types dependency to root package 408 + - [ ] Update use cases to import and return shared types 409 + - [ ] Update controllers to use shared types 410 + - [ ] Test backend compilation: `npm run type-check` 278 411 279 - ```typescript 280 - // TypeScript ensures the response matches GetUrlCardsResponse 281 - const result = await this.useCase.execute(query); 282 - return this.ok(res, result.value); // result.value is GetUrlCardsResponse 283 - ``` 412 + ### Phase 5: Development Setup ✅ 413 + - [ ] Add development scripts to root package.json 414 + - [ ] Test concurrent development: `npm run dev` 415 + - [ ] Verify hot reload works for type changes 284 416 285 - 3. **Use case returns typed response:** 286 - 287 - ```typescript 288 - // Return type is enforced by TypeScript 289 - return ok({ 290 - cards: [...], 291 - pagination: {...}, 292 - sorting: {...} 293 - }); // Must match GetUrlCardsResponse exactly 294 - ``` 417 + ### Phase 6: Validation ✅ 418 + - [ ] Run full type check across all packages 419 + - [ ] Test API endpoints return correct types 420 + - [ ] Add runtime validation (optional) 421 + - [ ] Update documentation 295 422 296 423 ## Best Practices 297 424 298 425 ### Type Naming Conventions 299 - 300 426 - **Requests**: `{Action}{Resource}Request` (e.g., `GetUrlCardsRequest`) 301 427 - **Responses**: `{Action}{Resource}Response` (e.g., `GetUrlCardsResponse`) 302 428 - **Common types**: Descriptive names (e.g., `User`, `Pagination`) 303 429 304 - ### File Organization 305 - 306 - - Keep related types together in the same file 307 - - Use barrel exports (`index.ts`) for clean imports 308 - - Separate common types from endpoint-specific types 430 + ### Development Workflow 431 + 1. **Always run shared types in watch mode** during development 432 + 2. **Make type changes first** before implementing features 433 + 3. **Use TypeScript strict mode** to catch issues early 434 + 4. **Version shared types** when making breaking changes 309 435 310 436 ### Error Handling 311 - 312 - - Define error response types consistently 437 + - Define consistent error response types 313 438 - Use discriminated unions for different error types 314 439 - Include error codes and messages in shared types 315 440 316 - ### Versioning 317 - 318 - - Consider API versioning in type names if needed 319 - - Use semantic versioning for breaking changes 320 - - Document breaking changes in migration guides 321 - 322 441 ## Troubleshooting 323 442 324 443 ### Common Issues 325 444 326 - 1. **Import path errors**: Ensure relative paths are correct 327 - 2. **Circular dependencies**: Keep shared types pure (no business logic) 328 - 3. **Type mismatches**: Use TypeScript strict mode to catch issues early 445 + 1. **"Cannot find module '@annos/shared-types'"** 446 + - Run `npm install` in root to setup workspace links 447 + - Ensure shared types are built: `npm run build:shared` 448 + 449 + 2. **Type mismatches between frontend and backend** 450 + - Check that both are using the same version of shared types 451 + - Rebuild shared types: `npm run build:shared` 452 + 453 + 3. **Hot reload not working for type changes** 454 + - Ensure `npm run dev:shared` is running in watch mode 455 + - Restart development servers if needed 329 456 330 457 ### Debugging Tips 331 458 332 - 1. Use `tsc --noEmit` to check types without building 333 - 2. Enable strict TypeScript settings in `tsconfig.json` 334 - 3. Use IDE features to trace type definitions 459 + 1. Use `npm ls @annos/shared-types` to check workspace linking 460 + 2. Check `src/shared/dist/` for compiled output 461 + 3. Use IDE "Go to Definition" to verify imports are resolving correctly 335 462 336 463 ## Future Enhancements 337 464 ··· 339 466 - **Runtime validation**: Add Zod schemas for all shared types 340 467 - **Documentation**: Auto-generate API docs from types 341 468 - **Testing**: Create type-safe test utilities 469 + - **Publishing**: Publish shared types to private npm registry 470 + - **Versioning**: Implement semantic versioning for breaking changes