# API Documentation Pattern **Last Updated:** 2026-01-31 **Confidence:** High ## Summary Episteme uses a "define once, document everywhere" approach. Rust types are the single source of truth for both runtime behavior and API documentation. The `utoipa` crate generates OpenAPI 3.1 specs at compile time from derive macros on the same structs used for JSON serialization. **Key Facts:** - `utoipa` chosen over `aide` for compile-time generation and framework-agnostic design - `utoipa-axum` provides `OpenApiRouter` that collects handler metadata automatically - `utoipa-swagger-ui` serves interactive docs in dev mode - `widdershins` converts OpenAPI JSON to Slate-compatible markdown in CI - No separate schema files to maintain; types ARE the schema ## The Stack | Layer | Tool | Purpose | |-------|------|---------| | Type definitions | `serde` + `utoipa::ToSchema` | Single struct defines JSON shape AND OpenAPI schema | | Handler docs | `#[utoipa::path]` | Documents endpoint alongside implementation | | Router | `utoipa_axum::OpenApiRouter` | Collects all handler metadata automatically | | Spec output | `/api-doc/openapi.json` | OpenAPI 3.1 spec, no build step | | Dev UI | `utoipa-swagger-ui` | Interactive API explorer at `/swagger-ui` | | Published docs | `widdershins` + Slate | CI generates static docs from OpenAPI spec | ## Adding a New Endpoint (Quick Reference) 1. Define DTO types with `#[derive(Serialize, Deserialize, ToSchema)]` 2. Write handler function 3. Annotate with `#[utoipa::path(post, path = "/foo", ...)]` 4. Register on `OpenApiRouter` - docs update automatically 5. No manual schema sync needed **File Pointer:** `.claude/guides/backend/api-endpoints.md` for full procedure. ## Why utoipa Over aide - Compile-time generation fits zero-cost philosophy - Framework-agnostic (future gRPC won't require rewrite) - Larger ecosystem (SwaggerUI, Redoc, Scalar, RapiDoc crates) - Already macro-heavy codebase (rkyv derives) - one more derive is natural ## gRPC (Future) When added, `.proto` files will be the source for gRPC docs via `protoc-gen-doc`. Both HTTP and gRPC will share the same internal types via `From<>` conversions. The DTO layer keeps internal `rkyv` types separate from public API contracts. ## Related Topics - [API Service](../services/api.md) - Endpoints and crate structure - [Error Handling](./error-handling.md) - thiserror + context pattern