Skip to content

feat: OpenAPI 3.1 spec + Scalar UI (Phase 1.b.3)#4

Merged
InstaZDLL merged 3 commits into
mainfrom
feat/1.b.3-openapi-scalar
May 30, 2026
Merged

feat: OpenAPI 3.1 spec + Scalar UI (Phase 1.b.3)#4
InstaZDLL merged 3 commits into
mainfrom
feat/1.b.3-openapi-scalar

Conversation

@InstaZDLL
Copy link
Copy Markdown
Owner

@InstaZDLL InstaZDLL commented May 30, 2026

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)

  • `utoipa` 5 with `axum_extras`
  • `utoipa-axum` 0.2 — the `OpenApiRouter` integration
  • `utoipa-scalar` 0.3 with `axum` — Scalar API reference

`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

  • Per-module routers in `src/api/*.rs` now return `utoipa_axum::OpenApiRouter`.
  • `api::router` merges them all.
  • `lib::app` seeds an `OpenApiRouter` with `ApiDoc::openapi()`, merges the API surface, and splits into `(Router, OpenApi)` for axum + spec consumption.

New endpoints

Path What
`/openapi.json` OpenAPI 3.1 doc as JSON
`/reference` Scalar API reference UI (Stripe-style, dark mode, ~500 KB)

Both stay outside `/api/v1/*` so a future Better-Auth middleware (1.d) gates only the data routes.

Existing handlers annotated

  • `/health` — `responses((status = 200, body = HealthResponse))`, tag `probes`
  • `/ready` — `responses((status = 200, ...), (status = 503, ...))`, tag `probes`. The 503 case is part of the contract (it's a 503-as-data API).

`HealthResponse` and `ReadyResponse` now derive `ToSchema`.

Tests

Two new 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.

Test plan

  • `cargo check --all-targets` ✅
  • `cargo fmt --check` ✅
  • `cargo clippy --all-targets --all-features -- -D warnings` ✅
  • CI Linux: full `cargo test` against Postgres 17 service container.
  • Manual: visit `http://localhost:3000/reference\` in a browser and confirm the spec renders.

Out of scope

  • CRUD endpoints under `/api/v1/*` (1.b.4)
  • Auth middleware (1.b.4 dev / 1.d real JWT)

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

    • Endpoint /openapi.json : spécification OpenAPI 3.1 générée automatiquement.
    • Endpoint /reference : interface interactive pour consulter la doc API.
    • Endpoints de santé (/health, /ready) exposés et documentés dans la spec.
  • Tests

    • Ajout de tests de fumée vérifiant la spec OpenAPI et le montage de l’UI Scalar.
  • Documentation

    • Ajout d’un guide de contribution/configuration et mise à jour du README (Phase 1.b.3).
  • Chores

    • Ajout de dépendances pour la génération OpenAPI côté serveur.

Review Change Stack

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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

📝 Walkthrough

Walkthrough

La PR ajoute génération OpenAPI 3.1 et UI Scalar, annote les endpoints /health et /ready, compose un OpenApiRouter qui sert /openapi.json (spec) et /reference (UI), et ajoute des tests fumée vérifiant la spec et le rendu Scalar.

Changes

OpenAPI 3.1 et Scalar UI

Layer / File(s) Résumé
Dépendances et configuration OpenAPI
Cargo.toml, src/lib.rs
Ajout de utoipa, utoipa-axum, utoipa-scalar. Définition de ApiDoc (#[derive(OpenApi)]) et exposition de OPENAPI_JSON_PATH et SCALAR_PATH.
Intégration OpenAPI sur les endpoints de sonde
src/api/health.rs, src/api/ready.rs, src/api/mod.rs
HealthResponse et ReadyResponse deviennent pub et dérivent ToSchema. Handlers décorés par #[utoipa::path(...)]. Les routers retournent OpenApiRouter et enregistrent les routes via routes!.
Montage du router et servage des endpoints OpenAPI/Scalar
src/lib.rs
Construction du router à partir de ApiDoc::openapi(), fusion des routes applicatives, montage de l’UI Scalar à /reference, et ajout de la route GET /openapi.json qui sérialise la spec avec gestion d’erreur.
Tests de validation du contrat et UI Scalar
tests/openapi.rs
Deux tests : openapi_doc_lists_every_handler (vérifie présence de /health et /ready et schémas de réponses) et scalar_ui_mounted (confirme que /reference retourne HTML contenant un marqueur "scalar").
Documentation du développeur et métadonnées projet
CLAUDE.md, README.md
Ajout de CLAUDE.md (setup, tests PostgreSQL, conventions, contributions) et mise à jour du README.md (Phase 1.b.3, documentation des endpoints /openapi.json et /reference).

Sequence Diagram

sequenceDiagram
  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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

📖 Sphinx documenté se réveille enfin,
OpenAPI et Scalar main dans la main,
/reference brille, spec mise au jour,
Les probes chantent leur schéma pour toujours,
Le serveur publie sa carte au contour.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Le titre décrit précisément le changement principal : ajout de la génération OpenAPI 3.1 et de l'UI Scalar, ainsi que la progression de phase de versioning.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1.b.3-openapi-scalar

Comment @coderabbitai help to get the list of available commands and usage tips.

@InstaZDLL InstaZDLL self-assigned this May 30, 2026
`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>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 00e9e72 and b7bc370.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock, !*.lock
📒 Files selected for processing (8)
  • CLAUDE.md
  • Cargo.toml
  • README.md
  • src/api/health.rs
  • src/api/mod.rs
  • src/api/ready.rs
  • src/lib.rs
  • tests/openapi.rs

Comment thread README.md Outdated
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant