Advanced

Auth schemes, remote specs, cache behavior, cleanOutput, dateTimeConverter, supported OpenAPI features, name collisions, and known limitations.

This page covers advanced configuration and features. If you haven't completed the basics yet, start with the Getting Started guide.

Authentication

The generator reads securitySchemes from your OpenAPI spec and emits static factory methods on the aggregator class — one factory per scheme type found. Pass the factory's return value via the interceptors: constructor parameter.

Bearer Token

Emitted when the spec declares type: http, scheme: bearer. The factory returns a Dio Interceptor that sets Authorization: Bearer <token> on every request.

final client = ExampleApiClient(
  interceptors: [
    ExampleApiClient.bearerAuth('your-bearer-token'),
  ],
);

Generated factory:

static Interceptor bearerAuth(String token) => InterceptorsWrapper(
      onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
        options.headers['Authorization'] = 'Bearer $token';
        handler.next(options);
      },
    );

Do not hardcode tokens in version-controlled code. Retrieve them from environment variables or secure storage at runtime.

API Key (Header)

Emitted when the spec declares type: apiKey, in: header. The headerName defaults to the name field from the spec's security scheme definition, or 'X-Api-Key' if absent.

ExampleApiClient(
  interceptors: [
    ExampleApiClient.apiKeyAuth('my-key', headerName: 'X-Api-Key'),
  ],
);

Generated factory:

static Interceptor apiKeyAuth(String apiKey,
        {String headerName = 'X-Api-Key'}) =>
    InterceptorsWrapper(
      onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
        options.headers[headerName] = apiKey;
        handler.next(options);
      },
    );

API Key (Query Parameter)

Emitted when the spec declares type: apiKey, in: query. The paramName defaults to the name field from the spec's security scheme definition, or 'api_key' if absent.

ExampleApiClient(
  interceptors: [
    ExampleApiClient.apiKeyQueryAuth('my-key', paramName: 'api_key'),
  ],
);

API keys in query parameters appear in server logs and browser history. Prefer header-based API keys or Bearer tokens when possible.

HTTP Basic Auth

Emitted when the spec declares type: http, scheme: basic. The factory base64-encodes username:password and sets Authorization: Basic <credentials> on every request. The aggregator file automatically imports dart:convert when this factory is emitted.

ExampleApiClient(
  interceptors: [
    ExampleApiClient.basicAuth('alice', 'secret'),
  ],
);

HTTP Basic auth transmits credentials on every request. Always use HTTPS and consider a more secure scheme (Bearer, OAuth) for production APIs.

RemoteSpec with Auth Headers

When your OpenAPI spec is served behind authentication (for example a private API gateway), use RemoteSpec with headers. The headers map is used only during code generation to fetch the spec — it does not appear in generated code. Auth header values are never logged, even with debugLogging: true.

lib/main.dart
@OpenApiGenerator(
  inputSpec: RemoteSpec(
    'https://example.com/api/openapi.json',
    headers: {'Authorization': 'Bearer my-codegen-token'},
  ),
  outputDir: 'lib/generated',
  clientName: 'MyApiClient',
)
class $MyApp {}

The url must use the https:// scheme. HTTP URLs are rejected at generation time. The generator also checks that any HTTP redirect target is still HTTPS before reading the response.

  • The headers map values must be compile-time string literals (the annotation is const).
  • A 30-second fetch timeout is enforced with no user-facing override in v0.1.0.

Cache Behavior

When skipIfSpecIsUnchanged: true (the default), the generator computes an MD5 cache key and skips code generation entirely on a cache hit.

The cache key is an MD5 hash of:

  1. The raw spec bytes (YAML or JSON, before parsing)
  2. The generator version string (kGeneratorVersion)
  3. A canonical JSON representation of the codegen-affecting annotation fields: outputDir, clientName, dateTimeConverter, and cleanOutput

Fields that do not affect generated output (debugLogging, skipIfSpecIsUnchanged, cachePath) are excluded from the key.

Cache location: <cachePath>/<key>/cache.json — one directory per unique key. Default cachePath: .dart_tool/dart_openapi_generator_cache.

When is the cache invalidated?

  • The spec file changes (byte-level, not just mtime)
  • Any of the four codegen-affecting annotation fields change
  • The generator version bumps

Set skipIfSpecIsUnchanged: false to always run generation regardless of cache.

dateTimeConverter

Controls how string + format: date-time fields are serialized globally. The setting applies to every DateTime field in every generated model.

ValuefromJsontoJson
DateTimeConverter.iso8601 (default)DateTime.parse(json['field'] as String)instance.field.toIso8601String()
DateTimeConverter.timestampDateTime.fromMillisecondsSinceEpoch(json['field'] as int)instance.field.millisecondsSinceEpoch
@OpenApiGenerator(
  inputSpec: LocalSpec('openapi/api.yaml'),
  outputDir: 'lib/generated',
  dateTimeConverter: DateTimeConverter.timestamp,
)
class $MyApp {}

Changing dateTimeConverter invalidates the MD5 cache and triggers a full regeneration. Per-field overrides are not supported in v0.1.0.

cleanOutput Safety Rules

With cleanOutput: true (the default), the generator deletes files it created in a previous run before writing new output. Deletion is driven by <cachePath>/manifest.json — only files recorded in the manifest are deleted.

The generator refuses to write or clean if outputDir resolves to any of the following:

  • The filesystem root (/)
  • The project root (.)
  • lib — the entire source directory
  • Any path containing .. as a path component
  • A symlink target

If any condition is met, the build aborts with an error before writing any output.

@OpenApiGenerator(
  inputSpec: LocalSpec('openapi/api.yaml'),
  outputDir: 'lib/generated', // safe — not lib, not ., not /
  cleanOutput: true,          // default
)
class $MyApp {}

Do not set outputDir to a directory that contains hand-authored files. Generated file names could collide with authored file names on a subsequent run, overwriting the authored file. Use a dedicated subdirectory such as lib/generated/.

Set cleanOutput: false only during debugging — stale generated files can cause dart analyze errors if they reference schemas that have been removed from the spec.

Error Handling via DioException

Every generated service method throws DioException on non-2xx HTTP responses. Catch it to handle API errors:

try {
  final user = await client.users.getUser('user-id');
  print(user.name);
} on DioException catch (e) {
  final statusCode = e.response?.statusCode;
  final body = e.response?.data;
  print('API error $statusCode: $body');
}
  • DioException is also thrown for network errors (timeout, no connectivity). Check e.type to distinguish network errors from HTTP errors.
  • When a spec operation has non-2xx response codes, the generator emits a comment in the method body:
    // NOTE: only 2xx responses are handled. Add error handling for: 404, 422
    // Access error body via: (e as DioException).response?.data
    

Supported OpenAPI Features

Schemas

FeatureSupport
object (with properties, required, additionalProperties)Supported
array (with items)Supported
string, integer, number, boolean primitivesSupported
string + format: date-timeSupported (see dateTimeConverter)
enum (string, integer, number values)Supported
allOf (flat merge of object members)Supported
oneOf with discriminatorSupported
oneOf without discriminatorParsed, but fromJson throws UnimplementedError
anyOfNot supported in v0.1.0 — throws a parse error. Use oneOf with a discriminator instead.
Same-document $ref (#/components/schemas/…)Supported
External $ref (URL or file path)Not supported in v0.1.0
Recursive / circular $refSupported via late nullable fields
OpenAPI 3.0 nullable: trueSupported
OpenAPI 3.1/3.2 type: [T, 'null']Supported
Mixing nullable: true + type: [T, 'null'] on the same schemaRejected with a parse error
Schema name starting with a digitRejected with a parse error

Operations

FeatureSupport
get, post, put, patch, delete, head, options methodsSupported
trace methodParsed, routed via _dio.request
in: path parametersSupported (positional, URL-encoded)
in: query parametersSupported (required → named required, optional → named optional)
in: querystring parameters (OpenAPI 3.2)Supported — treated identically to in: query
in: header parametersSupported (optional String? named params merged at runtime)
in: cookie parametersNot emitted — a warning is logged; use Dio interceptors instead
requestBody with application/json contentSupported
requestBody on GET/HEAD/OPTIONSNot sent (HTTP semantics); a warning is logged
Inline request body schemasSupported — named <OperationId>Request
Inline response schemasSupported — named <OperationId>Response
Primary response selection200201 → lowest 2xx code
Operations with no 2xx responseReturn type is Future<void>; a warning is logged
OpenAPI 3.2 additionalOperations (non-standard verbs)Supported — emitted as _dio.request with Options(method: 'VERB')

Security Schemes

SchemeEmitted factory
type: http, scheme: bearerbearerAuth(String token)
type: http, scheme: basicbasicAuth(String username, String password)
type: apiKey, in: headerapiKeyAuth(String apiKey, {String headerName})
type: apiKey, in: queryapiKeyQueryAuth(String apiKey, {String paramName})
Any other type/schemeNo factory; a warning is logged

Name Collision Handling

The generator maintains a NameRegistry built in two passes from the parsed SpecDocument.

Class names (pass 1: collect, pass 2: convert + validate):

  • Component schema names → toPascalCase + Dart keyword escape
  • Inline request body schemas → <OperationId>Request (or <Method><PathSegments>Request)
  • Inline response schemas → <OperationId>Response (or <Method><PathSegments>Response)

If two different spec locations produce the same Dart class name after conversion, generation aborts with an OpenApiParseException that names both conflicting locations. The fix is to rename one schema or operation in the spec.

Field names:

  • Property names → toLowerCamelCase + Dart keyword escape
  • Dart reserved words (class, default, in, etc.) get a _ suffix — e.g. a spec property named class becomes the Dart field class_. The fromJson/toJson key remains 'class'.

File names:

  • Model files: models/<snake_case_class_name>.dart
  • Service files: services/<tag_snake_case>_api.dart
  • Tag-to-filename conversion lowercases and replaces spaces/hyphens with underscores (does not apply PascalCase splitting — PetStore tag → petstore_api.dart, not pet_store_api.dart).

Known Limitations

  • No anyOf support. Schemas using anyOf cause a parse error. Use oneOf with a discriminator as an alternative.
  • Same-document $ref only. External file or URL references are not resolved — they cause a parse error. All referenced schemas must be in components/schemas of the same spec file.
  • No oneOf without discriminator. The sealed class is generated but fromJson throws UnimplementedError at runtime. The spec must include a discriminator.propertyName.
  • in: cookie parameters are not generated. The generator logs a warning and recommends Dio interceptors for cookie-based auth.
  • requestBody on GET/HEAD/OPTIONS is not sent. A warning is logged; HTTP semantics prevent sending a body on these methods.
  • Per-field dateTimeConverter overrides are not supported. The setting applies globally.
  • No webhooks or root-level pathItems (OpenAPI 3.1/3.2). Only paths and components/schemas are processed.
  • Only application/json request/response bodies. Multipart, form-encoded, and other content types are not generated.

See also: Getting Started · Annotation Reference · Generated Code