OpenAPI 3.1 documentation for Dart servers, with adapters for Relic, Shelf, and Serverpod 4 web-server routes.
axle is a small CLI plus a thin runtime. Annotate top-level handlers and DTOs, run the generator, and serve the result:
build/openapi.jsonfor client generators and tooling.build/openapi.yamlas the YAML equivalent of the same document.assets/swagger-ui/for self-hosted Swagger UI.lib/src/generated/openapi.g.dartas aconststring for AOT-safe serving.GET /openapi.jsonandGET /docsthrough an adapter.
The generator uses package:analyzer, so the spec is built from your Dart source
without dart:mirrors or a runtime registry.
- Security schemes (
http,apiKey,oauth2,openIdConnect), global defaults, per-route requirements, scopes, and public route overrides. - Response headers, cookie parameters, multiple request/response media types, and multipart bodies with binary file properties.
- Parameter and property constraints: formats, enums, numeric 8000 bounds, string lengths, and patterns.
- Explicit
oneOfschemas with discriminator mappings. - Global tags, external docs, terms of service, and optional servers.
- Relic and Shelf examples now exercise these features against live handlers, including real API key checks, content negotiation, and multipart uploads.
The package changelogs list every change:
- Models:
json_serializable,freezed, anddart_mappableannotations, custom converters, and@ApiProperty(type:). - Generic DTOs, sealed classes as
oneOf, and explicitallOfandanyOf. @ApiRouteon methods and top-level variables, not only top-level functions.defaultand4XXresponses, more response shortcuts, parameterstyleandexplode, and namedexamples.- Errors with
file:line:columnlocations, all reported in one run. build.yamlserializer options that change names or fields (field_rename,ignore_unannotated,caseStyle,enumCaseStyle) are read, and invalid or conflicting values fail the build.checkShelfRoutes, a test that every documented path reaches a handler, so@ApiRoutepaths and the router cannot drift apart unnoticed.- Several media types per status, several request bodies, and raw bytes as
format: binary, for downloads, uploads, and content negotiation. - A showcase API with 50 operations and 20 media types, tested against its own generated spec.
Sections below that describe these features need 0.3 or later. The roadmap lists each version.
| Package | Purpose |
|---|---|
axle_core |
Annotations, OpenAPI 3.1 document model, AxleDocs, and Swagger UI HTML. No framework dependencies. |
axle_cli |
axle init and axle gen, analyzer-based source scanning, JSON/YAML/Dart artifacts, and self-hosted Swagger UI assets. |
axle_relic |
Relic adapter for mountAxleDocs(...) and standalone handlers. Re-exports core for Relic apps. |
axle_shelf |
Shelf adapter with composable handlers and local-asset support. |
axle_serverpod |
Serverpod adapter for web-server routes. Serving only; RPC endpoints are out of scope. |
Add the packages to a Relic project:
dependencies:
axle_relic: ^0.3.0
relic: ^2.0.0-rc.2
dev_dependencies:
axle_cli: ^0.3.0axle_relic re-exports axle_core's annotations and document model, so Relic
apps only need axle_relic plus the CLI. axle_core remains a standalone
dependency for other framework adapters.
Relic 2.0.0 is currently a release candidate. Pin ^2.0.0-rc.2 until the stable
2.x release is available, then update to ^2.0.0.
This workspace is the
0.3.0release line. Use the published0.3.xpackages, orpath:dependencies while developing against the repository workspace.
Create the project configuration:
dart run axle_cli:axle initinit seeds axle.yaml from pubspec.yaml. After that, axle.yaml is the
source of truth for global API identity and generator paths; gen never reads
pubspec.yaml.
openapi: 3.1.0
info:
title: Todo API
version: 0.2.0
description: A Relic Todo API.
generate:
scan:
- lib
output:
json: build/openapi.json
ui: assets/swagger-ui
dart: lib/src/generated/openapi.g.dartGlobal OpenAPI metadata is opt-in: servers, tags, externalDocs, and
security can be declared in axle.yaml. Route-level tags and security stay on
@ApiRoute. Security metadata describes the contract; the application still
enforces auth.
generate.output.json is the spec path setting. The YAML file is derived by
replacing .json with .yaml, so the two artifacts cannot drift in config.
generate.output.ui is the directory where axle gen writes the self-hosted
Swagger UI assets; it defaults to assets/swagger-ui when omitted.
Annotate a top-level handler and its DTOs:
import 'package:axle_relic/axle_relic.dart';
@ApiSchema(description: 'A user returned by the API.')
class UserDto {
final int id;
final String name;
const UserDto({required this.id, required this.name});
}
@ApiRoute(
method: ApiMethod.get,
path: '/users/:id',
summary: 'Get a user',
tags: ['Users'],
pathParams: [ApiPathParam('id', type: int)],
responses: [
ApiResponse.ok(UserDto),
ApiResponse.notFound(),
],
)
Future<Result> getUser(Request request) async { ... }Generate the artifacts:
dart run axle_cli:axle genMount the docs and spec:
import 'package:axle_relic/axle_relic.dart';
import 'package:relic/relic.dart';
import 'package:your_package/src/generated/openapi.g.dart';
final app = RelicApp();
app.mountAxleDocs(
spec: axleOpenApiJson,
swaggerUiDirectory: 'assets/swagger-ui',
);
await app.serve();A complete runnable example is in
packages/axle_relic/example. It includes
protected /todos/stats and /todos/import routes, response headers, cookies,
application/vnd.axle+json negotiation, constraints, a oneOf activity
endpoint, and multipart upload handling.
For Shelf, depend on axle_shelf and shelf, then mount the docs handler:
import 'package:axle_shelf/axle_shelf.dart';
final handler = axleDocsHandler(
spec: axleOpenApiJson,
swaggerUiDirectory: 'assets/swagger-ui',
);See packages/axle_shelf for composition and local-asset
examples, including a complete runnable Todo API in
packages/axle_shelf/example with the same
security, headers, cookies, content negotiation, constraints, oneOf, and
multipart coverage.
dart run axle_cli:axle init [--project <dir>] [--force]
dart run axle_cli:axle gen [--project <dir>] [--config axle.yaml]
dart run axle_cli:axle gen --checkinit writes axle.yaml and prints the setup steps. It does not edit your
pubspec.yaml or server.dart. gen --check fails when the generated files are
out of date, which is useful in CI.
openapi.json,openapi.yaml, the Swagger UI asset directory, and the Dart constant from one generation run.- Component schemas from referenced DTO classes, including nested classes, enums,
List/Set,Map<String, T>, nullability,DateTime,Uri,Duration,BigInt, andUint8List/ByteData. @JsonKey(name: ...)and@ApiProperty(name: ...)property names.- Path, query, header, and cookie parameters, JSON/multipart request bodies, responses, response headers, and multiple media types.
- Parameter and property constraints: formats, enums, min/max, length, and patterns.
- Security schemes (
http,apiKey,oauth2,openIdConnect) and per-route security requirements. oneOf,anyOfandallOfschemas, and sealed classes asoneOfof their subtypes.- Generic DTOs (
Page<UserDto>becomes thePage_UserDtoschema). json_serializable,freezedanddart_mappablemodels, including renaming rules, defaults, and freezed unions.- Response keys beyond exact statuses:
defaultand ranges such as4XX, plus shortcuts for 401, 403, 409, 422, and 429. - Parameter
styleandexplode, and namedexampleson parameters, bodies, and responses. - Deterministic path and schema ordering for clean diffs.
Define reusable schemes in axle.yaml:
security:
schemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
default:
- bearerAuthThen opt routes in or out:
@ApiRoute(
method: ApiMethod.get,
path: '/me',
security: [ApiSecurityRequirement(['bearerAuth'])],
responses: [ApiResponse.ok(UserDto)],
)
Future<Result> me(Request request) async { ... }
@ApiRoute(
method: ApiMethod.get,
path: '/health',
security: [],
responses: [ApiResponse.ok(HealthDto)],
)
Future<Result> health(Request request) async { ... }security: [] makes one operation public. Multiple requirements are
alternatives (OR); multiple schemes in one requirement must all be satisfied
(AND). Swagger UI shows the Authorize flow automatically. The adapter does not
enforce auth; keep using framework middleware.
axle reads the annotations of json_serializable, freezed, and
dart_mappable, so the schema follows the wire format. Each has a runnable
example that checks the spec against the real JSON. See
Models and serialization.
axle_cli resolves next to json_serializable and dart_mappable_builder.
freezed 3.x needs an older analyzer, so install the CLI globally there:
dart pub global activate axle_cli.
- Annotate top-level functions, top-level variables that hold closures, or methods of classes and mixins. Closures passed inline to a router cannot carry annotations.
- Annotations describe the API; they do not bind requests, validate input, or serialize responses. Use your server framework for that.
- Wildcard routes (
*,/**) cannot be represented in OpenAPI. Addignore: trueto their annotation. - Relic (1.2.0 and the 2.0.0 release candidate), Shelf, and Serverpod 4.x are the current adapter targets. The Dart Frog adapter is not included yet.
allOf/anyOfare explicit only (@ApiSchema(allOf: ...)); axle does not infer them from inheritance.- Security metadata is declarative only. Auth enforcement, token validation, and middleware remain application-owned.
The shared adapter contract is AxleDocs. Adapters
serve the generated spec and UI; they do not bind routes or serialize responses.
Use axle_relic, axle_shelf, or axle_serverpod as the reference
implementations.
axle gen writes Swagger UI 5.34.0 into generate.output.ui. Pass that
directory to an adapter to self-host the UI; no extra asset package is required.
The CDN URL remains available as an opt-in fallback and is pinned with SRI
hashes.
Other packages cover parts of this space: shelf_open_api_generator (Shelf,
build_runner), dart_frog_open_api and dart_frog_swagger (Dart Frog). axle
targets OpenAPI 3.1 without build_runner, but has no Dart Frog adapter yet.
See the comparison for details and
where the others are ahead.
The repository ships an agent skill that walks from installation to generated OpenAPI 3.1 docs and verification:
npx skills add infm5/axle --skill axle-openapi-setupIt covers Relic, Shelf, Serverpod web routes, custom adapters, DTOs, security,
response headers, media types, multipart uploads, oneOf, docs quality checks,
and troubleshooting. See skills/axle-openapi-setup.
See docs/axle-roadmap.md for the full design, scope,
current status, and roadmap.
dart pub get
dart run melos run check # analyze + test + format + spec freshnessUseful individual scripts:
dart run melos run analyze
dart run melos run test
dart run melos run format
dart run melos run gen-check
dart run melos run coverage # writes coverage/lcov.info for Codecov