# 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`