feat: OpenAPI 3.1 spec + Scalar UI (Phase 1.b.3)#4
Conversation
Wire utoipa so the OpenAPI document stays in lockstep with the routes, and serve a modern API reference UI on top. What's wired: - `utoipa` + `utoipa-axum` + `utoipa-scalar` added to Cargo.toml. - `ApiDoc` in lib.rs declares the spec shell (title, description, AGPL license, `probes` tag). Every per-handler path comes from `#[utoipa::path]` annotations + `utoipa_axum::routes!()` — there's no parallel `paths(...)` list to keep in sync. - `api::router` now returns `utoipa_axum::OpenApiRouter`, and the per- module routers do too. `lib::app` seeds an `OpenApiRouter` with `ApiDoc::openapi()`, merges the API surface, and splits into `(Router, OpenApi)` for axum + spec consumption. - `GET /openapi.json` serves the OpenAPI 3.1 doc as JSON. - `GET /reference` renders the Scalar API reference UI from the same spec. Stripe-style, dark-mode native, integrated search, ~500 KB bundle. Both endpoints stay outside `/api/v1/*` so a future Better- Auth middleware (1.d) gates only the data routes. - `/health` and `/ready` carry `#[utoipa::path]` with `responses(...)` declaring their status + body shapes. The `ReadyResponse` 503 case is documented as part of the contract (it's a 503-as-data API). Integration tests in `tests/openapi.rs`: - `openapi_doc_lists_every_handler` — the spec must include /health, /ready, and the /ready operation must declare both 200 and 503. Catches a future refactor that drops `#[utoipa::path]` silently. - `scalar_ui_mounted` — `/reference` serves HTML containing the Scalar template marker. Catches a 404 that's otherwise invisible until someone opens a browser. Also includes a `CLAUDE.md` summarising the repo's conventions — useful for Claude Code sessions and human onboarding alike. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
📝 WalkthroughWalkthroughLa PR ajoute génération OpenAPI 3.1 et UI Scalar, annote les endpoints ChangesOpenAPI 3.1 et Scalar UI
Sequence DiagramsequenceDiagram
participant ApiDoc
participant OpenApiRouter as OpenApiRouter
participant HealthEndpoint
participant ReadyEndpoint
participant SpecRoute as GET_/openapi.json
participant ScalarUI as GET_/reference
participant Client
ApiDoc->>OpenApiRouter: ApiDoc::openapi() fournit la spec
OpenApiRouter->>HealthEndpoint: découvre #[utoipa::path] (health)
OpenApiRouter->>ReadyEndpoint: découvre #[utoipa::path] (ready)
Client->>SpecRoute: GET /openapi.json
SpecRoute->>SpecRoute: sérialise la spec en JSON
SpecRoute-->>Client: OpenAPI JSON
Client->>ScalarUI: GET /reference
ScalarUI->>SpecRoute: récupère /openapi.json
ScalarUI-->>Client: HTML Scalar rendu
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
`cargo fmt --all --check` failed on CI because the spec["paths"] chain in `openapi_doc_lists_every_handler` was multi-line where rustfmt wanted a single line. Format the file and re-run locally. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Line 34: The README claim that "adding an endpoint adds it to the spec
automatically" is too absolute; update the description for GET /openapi.json to
state that the OpenAPI spec is generated from #[utoipa::path] annotations on
handlers and only includes endpoints that are both annotated and registered with
the router (i.e., ensure you mention #[utoipa::path] and route
registration/registration of the handler so contributors know both are
required).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 89c231ec-5f94-40cb-85ae-a0555ad1ea3d
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock,!Cargo.lock,!*.lock
📒 Files selected for processing (8)
CLAUDE.mdCargo.tomlREADME.mdsrc/api/health.rssrc/api/mod.rssrc/api/ready.rssrc/lib.rstests/openapi.rs
The previous wording made it sound like adding an endpoint alone shows up in the spec. Two ingredients are actually required: 1. `#[utoipa::path(...)]` on the handler 2. `routes!()` registration on the per-module `OpenApiRouter` A bare `Router::route()` would mount the handler but leave it absent from `/openapi.json`. Spell out both so contributors don't end up with a silently-missing endpoint. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Summary
Phase 1.b.3 — wire utoipa so the OpenAPI document stays in lockstep with the routes, and serve a modern API reference UI on top.
What's wired
Dependencies (Cargo.toml)
`ApiDoc` (lib.rs)
Declares the spec shell only: title, description, AGPL license, the `probes` tag. No `paths(...)` list — every endpoint comes from `#[utoipa::path]` annotations + `utoipa_axum::routes!()`, so adding a handler adds it to the spec automatically.
Router rewiring
New endpoints
Both stay outside `/api/v1/*` so a future Better-Auth middleware (1.d) gates only the data routes.
Existing handlers annotated
`HealthResponse` and `ReadyResponse` now derive `ToSchema`.
Tests
Two new integration tests in `tests/openapi.rs`:
Test plan
Out of scope
Also includes a `CLAUDE.md` summarising the repo's conventions — useful for Claude Code sessions and human onboarding alike.
Signed-off-by: InstaZDLL github.105mh@8shield.net
Summary by CodeRabbit
Nouvelles Fonctionnalités
Tests
Documentation
Chores