8000 GitHub - infm5/axle: Framework agnostic OpenAPI specs and Docs(Swagger) generator for Dart 🎯 · GitHub
[go: up one dir, main page]

Skip to content
infm5Public

About

Framework agnostic OpenAPI specs and Docs(Swagger) generator for Dart 🎯

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

axle

CI codecov pub package Dart SDK License: MIT

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.json for client generators and tooling.
  • build/openapi.yaml as the YAML equivalent of the same document.
  • assets/swagger-ui/ for self-hosted Swagger UI.
  • lib/src/generated/openapi.g.dart as a const string for AOT-safe serving.
  • GET /openapi.json and GET /docs through an adapter.

The generator uses package:analyzer, so the spec is built from your Dart source without dart:mirrors or a runtime registry.

What's new in 0.2

  • 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 oneOf schemas 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.

What's new in 0.3

The package changelogs list every change:

  • Models: json_serializable, freezed, and dart_mappable annotations, custom converters, and @ApiProperty(type:).
  • Generic DTOs, sealed classes as oneOf, and explicit allOf and anyOf.
  • @ApiRoute on methods and top-level variables, not only top-level functions.
  • default and 4XX responses, more response shortcuts, parameter style and explode, and named examples.
  • Errors with file:line:column locations, all reported in one run.
  • build.yaml serializer 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 @ApiRoute paths 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.

Packages

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.

Quick start

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.0

axle_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.0 release line. Use the published 0.3.x packages, or path: dependencies while developing against the repository workspace.

Create the project configuration:

dart run axle_cli:axle init

init 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.dart

Global 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 gen

Mount 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.

CLI

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 --check

init 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.

What is generated

  • 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, and Uint8List/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, anyOf and allOf schemas, and sealed classes as oneOf of their subtypes.
  • Generic DTOs (Page<UserDto> becomes the Page_UserDto schema).
  • json_serializable, freezed and dart_mappable models, including renaming rules, defaults, and freezed unions.
  • Response keys beyond exact statuses: default and ranges such as 4XX, plus shortcuts for 401, 403, 409, 422, and 429.
  • Parameter style and explode, and named examples on parameters, bodies, and responses.
  • Deterministic path and schema ordering for clean diffs.

Security

Define reusable schemes in axle.yaml:

security:
  schemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  default:
    - bearerAuth

Then 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.

Model packages

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.

Limitations

  • 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. Add ignore: true to 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/anyOf are 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.

Adapters

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.

Alternatives

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.

Agent skill

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-setup

It 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.

Development

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 freshness

Useful 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

About

Framework agnostic OpenAPI specs and Docs(Swagger) generator for Dart 🎯

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0