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.
@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
headersmap values must be compile-time string literals (the annotation isconst). - 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:
- The raw spec bytes (YAML or JSON, before parsing)
- The generator version string (
kGeneratorVersion) - A canonical JSON representation of the codegen-affecting annotation fields:
outputDir,clientName,dateTimeConverter, andcleanOutput
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.
| Value | fromJson | toJson |
|---|---|---|
DateTimeConverter.iso8601 (default) | DateTime.parse(json['field'] as String) | instance.field.toIso8601String() |
DateTimeConverter.timestamp | DateTime.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');
}
DioExceptionis also thrown for network errors (timeout, no connectivity). Checke.typeto 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
Schemas
| Feature | Support |
|---|---|
object (with properties, required, additionalProperties) | Supported |
array (with items) | Supported |
string, integer, number, boolean primitives | Supported |
string + format: date-time | Supported (see dateTimeConverter) |
enum (string, integer, number values) | Supported |
allOf (flat merge of object members) | Supported |
oneOf with discriminator | Supported |
oneOf without discriminator | Parsed, but fromJson throws UnimplementedError |
anyOf | Not 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 $ref | Supported via late nullable fields |
OpenAPI 3.0 nullable: true | Supported |
OpenAPI 3.1/3.2 type: [T, 'null'] | Supported |
Mixing nullable: true + type: [T, 'null'] on the same schema | Rejected with a parse error |
| Schema name starting with a digit | Rejected with a parse error |
Operations
| Feature | Support |
|---|---|
get, post, put, patch, delete, head, options methods | Supported |
trace method | Parsed, routed via _dio.request |
in: path parameters | Supported (positional, URL-encoded) |
in: query parameters | Supported (required → named required, optional → named optional) |
in: querystring parameters (OpenAPI 3.2) | Supported — treated identically to in: query |
in: header parameters | Supported (optional String? named params merged at runtime) |
in: cookie parameters | Not emitted — a warning is logged; use Dio interceptors instead |
requestBody with application/json content | Supported |
requestBody on GET/HEAD/OPTIONS | Not sent (HTTP semantics); a warning is logged |
| Inline request body schemas | Supported — named <OperationId>Request |
| Inline response schemas | Supported — named <OperationId>Response |
| Primary response selection | 200 → 201 → lowest 2xx code |
| Operations with no 2xx response | Return 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
| Scheme | Emitted factory |
|---|---|
type: http, scheme: bearer | bearerAuth(String token) |
type: http, scheme: basic | basicAuth(String username, String password) |
type: apiKey, in: header | apiKeyAuth(String apiKey, {String headerName}) |
type: apiKey, in: query | apiKeyQueryAuth(String apiKey, {String paramName}) |
| Any other type/scheme | No 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 namedclassbecomes the Dart fieldclass_. ThefromJson/toJsonkey 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 —
PetStoretag →petstore_api.dart, notpet_store_api.dart).
Known Limitations
- No
anyOfsupport. Schemas usinganyOfcause a parse error. UseoneOfwith a discriminator as an alternative. - Same-document
$refonly. External file or URL references are not resolved — they cause a parse error. All referenced schemas must be incomponents/schemasof the same spec file. - No
oneOfwithout discriminator. The sealed class is generated butfromJsonthrowsUnimplementedErrorat runtime. The spec must include adiscriminator.propertyName. in: cookieparameters are not generated. The generator logs a warning and recommends Dio interceptors for cookie-based auth.requestBodyon GET/HEAD/OPTIONS is not sent. A warning is logged; HTTP semantics prevent sending a body on these methods.- Per-field
dateTimeConverteroverrides are not supported. The setting applies globally. - No
webhooksor root-levelpathItems(OpenAPI 3.1/3.2). Onlypathsandcomponents/schemasare processed. - Only
application/jsonrequest/response bodies. Multipart, form-encoded, and other content types are not generated.
See also: Getting Started · Annotation Reference · Generated Code