chore(api): experimental REST API namespace (/api/experimental)
Proposed changes
Adds a general-purpose experimental REST API namespace at /api/experimental/.... Endpoints under it are explicitly unstable: they may change shape or be removed in any release without a major-version bump and without a deprecation cycle. The namespace is the contract — hitting /api/experimental/* is opting into instability — so /v1 keeps its implicit semver promise untouched.
This is a mechanism, not a feature: any team can register an experimental endpoint, gather real production usage, then either promote it to /v1 or drop it.
Implements docs/experimental-api-endpoints-plan.md, one commit per step:
feat(api): add experimental API instance— acreateApi({ version: 'experimental' })instance mounted at/api/experimental/<name>, typedAPIClass<'/experimental'>.feat(api): mount experimental router and metrics— mounts the router before the default catch-all and adds a dedicated metrics block so experimental traffic is recorded underversion=experimental(the canary used to decide promotion to/v1).feat(api): add experimental unstable-signal middleware— stamps every experimental response withx-experimental: trueand aWarning: 299 ...header (mirrors the deprecation-header pattern); registered onAPI.experimentalonly.feat(rest-typings): add opt-in ExperimentalEndpoints— a separateExperimentalEndpointstype, re-exported from the package root but deliberately not merged into theEndpointsunion, soPathPattern/Method/Pathand the stable typed client stay clean. Consumers import it explicitly.chore(api): add experimental guardrails and docs— a type-level CI guard that failsyarn typecheckif a path is declared in both unions (catches promotion-by-copy-paste), plus the developer guide and design plan.
Only the new typed API (.get()/.post()/.put()/.delete() with AJV validators) is usable on experimental routes — the deprecated .addRoute() path is not extended. Auth, permissions, rate limiting, CORS, and validation all come for free from the shared createApi + middleware chain.
Testing / verification
eslint --quietclean on every touched file.yarn typecheckclean inrest-typings; the overlap guard was verified negatively by temporarily declaring/v1/meinExperimentalEndpoints— typecheck failed atnoOverlapWithStableEndpoints.tsnaming the path — then restored clean.- Not yet run: the runtime end-to-end checklist (an actual
GET /api/experimental/<name>returning the headers), since no experimental endpoint exists yet. Worth confirming when the first one lands.
Further comments
- The unstable-signal middleware is registered at module load (not in
startRestAPI) so it precedes any route added later by endpoint files — Hono runs.usemiddleware in registration order. - Used a second
metricsMiddlewareblock rather than broadening the v1 regex, so theversionlabel stays accurate instead of mislabeling experimental traffic asv1. - Generated API docs intentionally skip experimental endpoints (they scan
Endpoints, which these are kept out of) — documented as a deliberate decision.
🤖 Generated with Claude Code
Summary by CodeRabbit
-
New Features
- Added documentation for experimental REST endpoints, including opt-in usage, lifecycle expectations, warnings, metrics, and promotion guidelines.
- Added opt-in TypeScript typings for experimental endpoints.
- Experimental API responses now include warning headers, including for errors and rejected preflight requests.
- Added path-specific metrics and improved handling for unmatched routes.
-
Tests
- Added coverage for experimental responses, CORS behavior, 404s, and metrics routing.