# API Contract Workflow **Single Source of Truth: Backend OpenAPI Specification** --- ## Overview This project uses **OpenAPI-driven development** where the backend C# API is the authoritative source for API contracts. The frontend Flutter app automatically generates type-safe Dart code from the OpenAPI specification. --- ## Architecture ``` Backend (C#) Frontend (Flutter/Dart) ───────────── ─────────────────────── Controllers with api-schema.json XML docs ──────────► (copied from backend) │ docs/openapi.json │ (auto-generated) ──────────► │ ▼ lib/api/generated/ (auto-generated types) │ ▼ lib/api/client.dart (CQRS API client) │ ▼ lib/api/endpoints/ (endpoint extensions) ``` --- ## Backend Responsibilities ### 1. XML Documentation All controllers and DTOs must have complete XML documentation: ```csharp /// Gets paginated users with filtering /// Page number (1-based) /// Returns paginated user list /// Unauthorized [HttpGet] [ProducesResponseType(typeof(PagedResult), 200)] [ProducesResponseType(401)] public async Task GetUsers([FromQuery] int page = 1) { } ``` ### 2. OpenAPI Export Backend generates `docs/openapi.json`: ```bash cd backend dotnet run --project Codex.Api & sleep 5 curl https://localhost:7108/swagger/v1/swagger.json > docs/openapi.json pkill -f "Codex.Api" ``` ### 3. Schema Distribution Frontend copies `docs/openapi.json` to `api-schema.json`: ```bash cp ../backend/docs/openapi.json ./api-schema.json ``` --- ## Frontend Responsibilities ### 1. Install Dependencies OpenAPI generator packages are in `pubspec.yaml`: ```yaml dependencies: http: ^1.2.2 json_annotation: ^4.9.0 dev_dependencies: build_runner: ^2.4.14 json_serializable: ^6.9.2 openapi_generator_annotations: ^5.0.1 ``` ### 2. Code Generation Generate Dart types from OpenAPI spec: ```bash flutter pub run build_runner build --delete-conflicting-outputs ``` ### 3. Generated Output Code is generated to `lib/api/generated/`: - ✅ **DO NOT EDIT** - These files are auto-generated - ✅ **DO NOT COMMIT** - Listed in `.gitignore` - ✅ **REGENERATE** on every API schema update ### 4. Manual Code (Stable) These files are **manually maintained**: - `lib/api/client.dart` - CQRS client framework - `lib/api/types.dart` - Core types (Result, ApiError, pagination) - `lib/api/endpoints/*.dart` - Endpoint-specific extensions --- ## Workflow: Making API Changes ### Backend Developer Flow 1. **Update C# code** with XML documentation 2. **Run API** to regenerate Swagger 3. **Export OpenAPI spec**: ```bash curl https://localhost:7108/swagger/v1/swagger.json > docs/openapi.json ``` 4. **Commit** `docs/openapi.json` to git 5. **Notify frontend** that API contract changed ### Frontend Developer Flow 1. **Pull latest** backend changes 2. **Copy schema**: ```bash cp ../backend/docs/openapi.json ./api-schema.json ``` 3. **Regenerate types**: ```bash flutter pub run build_runner build --delete-conflicting-outputs ``` 4. **Update endpoint code** if needed (new queries/commands) 5. **Test** with new types --- ## Type Safety Guarantees ### Strict Typing Rules All generated code follows project strict typing standards: - ✅ No `dynamic` types - ✅ No `any` types - ✅ Explicit type annotations everywhere - ✅ Null safety enforced ### Example: Generated Query Backend defines: ```csharp public record HealthQuery(); ``` Frontend generates: ```dart class HealthQuery { const HealthQuery(); Map toJson() => {}; factory HealthQuery.fromJson(Map json) => const HealthQuery(); } ``` --- ## Error Handling ### Backend Contract Backend returns structured errors: ```json { "message": "Validation failed", "statusCode": 422, "details": "Email is required" } ``` ### Frontend Handling Client wraps all responses in `Result`: ```dart final result = await client.executeQuery( endpoint: 'users/123', query: const GetUserQuery(), fromJson: UserDto.fromJson, ); result.when( success: (user) => print('Got user: ${user.name}'), error: (error) => print('Error: ${error.message}'), ); ``` --- ## File Structure ``` Console/ ├── api-schema.json # Copied from backend (DO NOT EDIT) ├── build.yaml # Code generation config ├── lib/api/ │ ├── client.dart # CQRS client (manual) │ ├── types.dart # Core types (manual) │ ├── generated/ # Auto-generated (git-ignored) │ │ └── .gitkeep │ └── endpoints/ │ └── health_endpoint.dart # Endpoint extensions (manual) └── .claude-docs/ └── api-contract-workflow.md # This file ``` --- ## Benefits ### For Backend - ✅ Single source of truth (C# code with XML docs) - ✅ Type-safe APIs enforced by compiler - ✅ Swagger UI for testing - ✅ Automatic client generation ### For Frontend - ✅ Type-safe API calls (no runtime errors) - ✅ Auto-completion in IDE - ✅ Compile-time validation - ✅ No manual type definitions - ✅ Always in sync with backend ### For Team - ✅ Clear contract boundaries - ✅ Breaking changes caught early - ✅ No API drift - ✅ Shared understanding via OpenAPI spec --- ## Troubleshooting ### "Generated code has type errors" **Solution:** Backend may have incomplete XML docs or invalid schema. Ask backend team to validate `openapi.json`. ### "Types don't match backend" **Solution:** Regenerate frontend types: ```bash cp ../backend/docs/openapi.json ./api-schema.json flutter pub run build_runner build --delete-conflicting-outputs ``` ### "Build runner fails" **Solution:** Clean and rebuild: ```bash flutter clean flutter pub get flutter pub run build_runner build --delete-conflicting-outputs ``` --- ## References - **OpenAPI Spec:** `api-schema.json` - **Backend Docs:** `../backend/docs/ARCHITECTURE.md` - **Strict Typing:** `.claude-docs/strict-typing.md` - **Response Protocol:** `.claude-docs/response-protocol.md`