tonik 0.10.0 copy "tonik: ^0.10.0" to clipboard
tonik: ^0.10.0 copied to clipboard

Pure Dart OpenAPI 3.0/3.1 code generator. Creates type-safe API client packages for Dart and Flutter with sealed classes, pattern matching, and full encoding support.


tonik logo

MIT License pub version pub likes stars on github tests zread

Tonik #

A Dart code generator for OpenAPI 3.0 and 3.1 specifications.

Generate type-safe API client packages for Dart and Flutter. Tonik produces idiomatic code with sealed classes for oneOf, exhaustive pattern matching for responses, and full OpenAPI encoding support.

Key Features #

Type-Safe Response Handling by Status Code and Content Type #

Most OpenAPI generators pick a single "success" type and a single "error" type per operation, silently discarding the response schemas for all other status codes. If your endpoint returns a Pet for 200, a ValidationError for 400, and a NotFoundError for 404, you only get the 200 type — the rest are lost or reduced to untyped error strings.

Tonik generates a distinct type for every response defined in your spec. Each status code and content type combination becomes its own strongly-typed class, and the compiler enforces exhaustive handling:

final response = await petApi.updatePet(body: pet);

switch (response) {
  case TonikSuccess(:final value):
    switch (value) {
      case UpdatePetResponse200(:final body):
        print('Updated: ${body.name}');
      case UpdatePetResponse400(:final body):
        print('Validation: ${body.message}');
      case UpdatePetResponse404(:final body):
        print('Not found: ${body.detail}');
    }
  case TonikError(:final error):
    print('Network error: $error');
}

Every response body is deserialized into the correct type for its status code — no casting, no type checks, no lost information. Add a new response to your spec, regenerate, and the compiler tells you every call site that needs updating.

Composition with Sealed Classes #

oneOf, anyOf, and allOf generate idiomatic Dart code:

  • oneOf - Sealed class with one subclass per variant
  • anyOf - Class with nullable fields for each alternative
  • allOf - Class with a field for each member schema

See Composite Data Types for usage examples.

No Name Conflicts #

Use Error, Response, List, or any Dart built-in as schema names. Tonik uses scoped code emission to properly qualify all type references - no naming collisions with dart:core or transport dependencies.

Integer and String Enums #

Both work out of the box, with optional unknown-value handling for forward compatibility:

status:
  type: integer
  enum: [0, 1, 2]
  x-dart-enum: [pending, active, closed]

All Parameter Encoding Styles #

Path, query, and header parameters support all OpenAPI styles: simple, label, matrix, form, spaceDelimited, pipeDelimited, and deepObject.

Pure Dart #

Install with dart pub global activate tonik and run. No JVM, no Docker, no external dependencies.

Choice of HTTP Backend #

Generate clients with Dio (the default) or package:http. Selection is generation-time and applies to the whole generated package; ordinary models, API calls, and result handling keep the same shape.

Platform Support #

Generated packages are pure Dart with no native plugin dependency. The current dual-backend compatibility suite targets the Dart VM. Browser behavior depends on the selected backend and is not part of the current compatibility certification.

Documentation #

Quick Start #

Install #

dart pub global activate tonik

Generate #

tonik --package-name=my_api --spec=openapi.yaml

Use #

Add the generated package to your project:

dart pub add my_api:{'path':'./my_api'}
dart pub add tonik_util

Then import and use:

import 'package:my_api/my_api.dart';
import 'package:tonik_util/tonik_util.dart';

final api = PetApi(CustomServer(baseUrl: 'https://api.example.com'));

final response = await api.getPetById(petId: 1);
switch (response) {
  case TonikSuccess(:final value):
    print('Response: $value');
  case TonikError(:final error):
    print('Failed: $error');
}

See the real server examples for Dart clients using FastAPI, NestJS, Fastify, Spring Boot, and Rails APIs.

Feature Summary #

Category What's Supported
Responses Multiple status codes, multiple content types and media-type ranges (type/*, */*), response headers, default and range codes (2XX)
Composition oneOf (sealed classes), anyOf, allOf, discriminators, nested composition
Types Integer/string enums, date, date-time with timezone, decimal/BigDecimal, uri, binary
Parameters Path, query, header; all encoding styles (form, simple, label, matrix, deepObject, etc.)
Request Bodies application/json, application/x-www-form-urlencoded, application/octet-stream, text/plain
Multipart multipart/form-data with primitive, file, JSON, and array parts; per-part encoding and content types
Schema readOnly/writeOnly enforcement, nullable, required, deprecated, boolean schemas, additionalProperties
Configuration HTTP backend selection, name overrides, filtering by tag/operation/schema, deprecation handling, content-type mapping, parallel generation tuning
OAS 3.1 $ref with siblings, $defs local definitions, boolean schemas, nullable type arrays, contentEncoding/contentMediaType

Acknowledgments #

Special thanks to felixwoestmann, without whom this project would not exist.

8
likes
160
points
1.33k
downloads

Documentation

API reference

Publisher

verified publisherottenweller.net

Weekly Downloads

Pure Dart OpenAPI 3.0/3.1 code generator. Creates type-safe API client packages for Dart and Flutter with sealed classes, pattern matching, and full encoding support.

Repository (GitHub)
View/report issues
Contributing

Topics

#openapi #codegen #swagger #rest #api-client

License

MIT (license)

Dependencies

args, collection, logging, meta, tonik_core, tonik_generate, tonik_parse, yaml

More

Packages that depend on tonik