Configuration¶
oapi-codegen uses YAML configuration files to control code generation behavior. This page documents all available configuration options.
Basic Usage¶
Create a configuration file (e.g., cfg.yaml) and reference it when running oapi-codegen:
go run github.com/doordash-oss/oapi-codegen-dd/v3/cmd/oapi-codegen --config cfg.yaml spec.yaml
Tip
Use the JSON schema for IDE autocomplete and validation:
# yaml-language-server: $schema=https://raw.githubusercontent.com/doordash-oss/oapi-codegen-dd/HEAD/configuration-schema.json
Configuration Options¶
Package Settings¶
package¶
Type: string | Default: "gen"
The Go package name for generated code.
package: myapi
copyright-header¶
Type: string | Default: "Code generated by oapi-codegen. DO NOT EDIT."
Header comment added to all generated files.
copyright-header: "Copyright 2024 My Company. Code generated by oapi-codegen. DO NOT EDIT."
skip-prune¶
Type: boolean | Default: false
When true, keeps all types from the spec even if they're not referenced by any operations. By default, unreferenced types are pruned.
skip-prune: true
Overlay Settings¶
overlay.sources¶
Type: string[] | Default: []
List of overlay files to apply to the OpenAPI spec before code generation. Each source can be a file path or URL. Overlays are applied in order.
overlay:
sources:
- ./overlays/add-go-names.yaml
- https://example.com/shared-overlay.yaml
See Overlays for detailed documentation and examples.
External File References¶
base-path¶
Type: string | Default: auto-detected from spec file path
Directory used to resolve relative $ref file references. When using the CLI with a local spec file, this is automatically set to the spec file's parent directory. Set this explicitly when using the library programmatically or when the spec references files relative to a different directory.
base-path: ./specs
This enables splitting large specs across multiple files:
# specs/api.yaml
components:
schemas:
User:
type: object
properties:
address:
$ref: './common.yaml#/components/schemas/Address'
Output Settings¶
output.use-single-file¶
Type: boolean | Default: true
Generate all code in a single file. When false, splits code into multiple files (types, client, etc.).
output:
use-single-file: false
Note
When use-single-file: false, the generator automatically creates a subdirectory named after the package value. For example, with package: api and directory: ".", files are written to ./api/. To control the exact output location, set directory explicitly (e.g., directory: api).
output.directory¶
Type: string | Default: "."
Directory where generated files should be placed.
output:
directory: "./generated"
Note
When use-single-file: false, the package name is appended as a subdirectory. For example, directory: "./generated" with package: api outputs to ./generated/api/. When use-single-file: true, files are written directly to the specified directory.
output.filename¶
Type: string | Default: "gen.go"
Filename to use when use-single-file: true.
output:
filename: "api.gen.go"
output.skip-fmt¶
Type: boolean | Default: false
Skip running goimports and gofmt on generated code. Only byte-order-mark sanitization is performed. Useful for faster generation or when using custom import handling (e.g., yaml v4 instead of v3).
output:
skip-fmt: true
Generation Settings¶
generate.client¶
Type: boolean | Default: false
Generate the classic HTTP client: one method per operation that returns the picked 2xx body type directly (e.g. func (c *Client) UploadDocument(...) (*DocumentStored, error)).
Use this when callers only need the response body and the operation has a single documented success status. Headers and non-picked 2xx bodies are not exposed by this style; for those, use generate.client-with-response instead, or both together.
generate:
client: true
See examples/client/example1/cfg.yaml for a complete example.
generate.client-with-response¶
Type: boolean | Default: false
Generate <Op>WithResponse sibling functions that return a typed envelope: one JSON<status> (or Text<status> / HTML<status> / etc.) field per documented response, one Headers<status> typed struct per status that declares headers, plus HTTPResponse *http.Response for raw access (undocumented headers, raw body bytes, status string, etc.).
Use this when an operation has multiple 2xx statuses with different bodies (e.g. 201 sync + 202 queued), or when callers need typed access to response headers like Location or Retry-After.
Status ranges and default get fields of their own: JSON4XX for 4XX, JSONDefault for default. Responses are matched as OpenAPI specifies, so an exact status wins over the range covering it, and both win over default: with 404, 4XX and default documented, a 404 fills JSON404, a 409 JSON4XX and a 503 JSONDefault. The returned error is nil only for a documented success. When default is the only success documented, it counts as one for 2xx statuses only. Earlier versions exposed a range under a single stand-in code, such as JSON400 for 4XX or JSON200 for 2XX; those fields remain as deprecated aliases of the new ones, unless an explicit code now owns the name.
Additive to generate.client. When both flags are true, the generated ClientInterface lists every classic method alongside its WithResponse sibling so a single mock or test double covers both shapes.
generate:
client: true
client-with-response: true
The four valid combinations:
client |
client-with-response |
Output |
|---|---|---|
false |
false |
No client (types/server only) |
true |
false |
Classic client only |
false |
true |
Envelope client only |
true |
true |
Both styles, combined into one ClientInterface |
See examples/responses/multiple/client-with-response/cfg.yaml for envelope-only and examples/responses/multiple/client-combined/cfg.yaml for the combined case.
generate.client-streaming¶
Type: boolean | Default: false
Generate a <Op>Stream sibling for every operation that documents a sequential
success response - text/event-stream, or the ndjson/jsonl family - returning a
live runtime.Stream[T] over the per-frame type instead of buffering the body.
generate:
client: true
client-streaming: true
The non-streaming methods keep their media type and their signatures, so an
operation declaring both application/json and text/event-stream at one
status exposes both shapes and the generator never has to pick a winner:
func (c *Client) Chat(ctx, options, ...) (*ChatResponse, error) // JSON
func (c *Client) ChatStream(ctx, options, ...) (*runtime.Stream[Chunk], error) // SSE
Additive to generate.client and
generate.client-with-response: each mode gets
the sibling it needs, so with the envelope client on you also get
<Op>StreamWithResponse and a Stream<status> field next to JSON<status>.
Off by default because the siblings add methods to the generated
ClientInterface, which would otherwise break hand-written mocks on upgrade.
With the flag off, generated output is byte-for-byte what it was before
streaming support existed.
So that the flag is not something you have to know about in advance, generation logs the operations it affects when the flag is off: a warning for those whose generated method will block because their only success media type is sequential, and an informational line for those that declare a sequential media type alongside a buffered one.
See the Streaming page for how to consume a stream, and examples/client/streaming for a full example.
generate.omit-description¶
Type: boolean | Default: false
Omit schema descriptions from generated code comments.
generate:
omit-description: true
generate.default-int-type¶
Type: string ("int" | "int32" | "int64") | Default: "int"
Default Go type to use for OpenAPI integer types without a format specifier.
generate:
default-int-type: int64
generate.always-prefix-enum-values¶
Type: boolean | Default: true
Prefix enum constants with the schema name to avoid naming conflicts.
generate:
always-prefix-enum-values: false
generate.models¶
Type: boolean | Default: true
Generate model types. Set to false when models are generated in a separate package and you only want to generate client or handler code.
generate:
models: false
generate.handler.output.overwrite¶
Type: boolean | Default: false
Force regeneration of scaffold-once files (e.g., service.go, middleware.go). Normally these files are only generated if they don't exist.
generate:
handler:
output:
overwrite: true
Validation Settings¶
generate.validation.skip¶
Type: boolean | Default: false
Skip generation of Validate() methods on types.
generate:
validation:
skip: true
generate.validation.simple¶
Type: boolean | Default: false
Use simple validation approach with validate.Struct() for all types.
generate:
validation:
simple: true
generate.validation.response¶
Type: boolean | Default: false
Generate Validate() methods for response types (useful for contract testing).
generate:
validation:
response: true
Handler/Server Generation¶
Generate server-side handler code with a service interface pattern. Supports multiple router frameworks.
generate.handler.kind¶
Type: string | Required
The router/framework to generate handler code for. Supported values:
chi- go-chi/chiecho- labstack/echofiber- gofiber/fibergin- gin-gonic/ginstd-http- Go standard librarynet/http
generate:
handler:
kind: chi
generate.handler.name¶
Type: string | Default: "Service"
Name of the generated service interface.
generate:
handler:
kind: chi
name: "APIService"
generate.handler.models-package¶
Type: object (path, alias) | Default: none
Package that model types live in, when they're generated separately
(generate.models: false) into a different package from the handler.
path is the Go import path (required); alias is the identifier used to
qualify model type references and defaults to the last segment of path.
Every model type reference in the generated handler is qualified, and the
import is added automatically.
generate:
models: false
handler:
kind: chi
models-package:
path: example.com/myapp/models
alias: models # optional
This generates models.User instead of User in the handler code. Both the
models run and the handler run must use the same spec, filter, and
error-mapping. See Server Generation
for the full two-step setup.
generate.handler.handler-package-alias¶
Type: string | Default: ""
Package alias used to reference the generated handler code from the service
scaffold (service.go), when the scaffold is generated into its own
package. Unrelated to models-package above - this qualifies handler-owned
symbols (ServiceInterface, <Op>ServiceRequestOptions), not model types.
generate:
handler:
kind: chi
handler-package-alias: server
This generates server.ServiceInterface instead of ServiceInterface in
service.go.
generate.handler.models-package-alias¶
Type: string | Default: ""
Deprecated: use handler-package-alias. Despite the name, this was
never the package models live in - it's the package the generated handler
itself is in, as seen from service.go. Kept as a fallback: if
handler-package-alias is unset, this value is used.
generate.handler.multipart-max-memory¶
Type: integer | Default: 32
Maximum memory in MB for multipart form parsing. Files exceeding this limit are stored in temporary files on disk.
generate:
handler:
kind: chi
multipart-max-memory: 64
generate.handler.validation.request¶
Type: boolean | Default: false
Enable validation of incoming requests in handlers.
generate:
handler:
kind: chi
validation:
request: true
generate.handler.validation.response¶
Type: boolean | Default: false
Enable validation of outgoing responses in handlers. Useful for contract testing.
generate:
handler:
kind: chi
validation:
response: true
generate.handler.output¶
Type: object | Default: uses root output settings
Output settings for scaffolded handler files (service.go, middleware.go).
generate:
handler:
kind: chi
output:
directory: api
package: api
generate.handler.middleware¶
Type: object | Default: null
Enable generation of middleware.go scaffold file. If not set, no middleware file is generated.
generate:
handler:
kind: chi
middleware: {}
generate.handler.server¶
Type: object | Default: null
Enable generation of a runnable server/main.go file.
generate:
handler:
kind: chi
server:
directory: server
port: 8080
timeout: 30
handler-package: github.com/myorg/myapi/api
| Property | Type | Default | Description |
|---|---|---|---|
directory |
string |
"server" |
Output directory for main.go |
port |
integer |
8080 |
Port the server listens on |
timeout |
integer |
30 |
Request timeout in seconds |
handler-package |
string |
required | Full import path of the handler package |
See examples/server/ for complete examples of handler generation with different frameworks.
Filtering¶
Filtering allows you to include or exclude specific parts of your OpenAPI specification during code generation. This is useful when you only need to generate code for a subset of your API.
How Filtering Works¶
- Include filters - Only generate code for matching items
- Exclude filters - Generate code for everything except matching items
- Precedence - Exclude filters take precedence over include filters
- Transitive pruning - When schema properties are filtered out, schemas that are only referenced by those properties are also pruned
Filter by Paths¶
Filter operations by API path patterns.
filter:
include:
paths:
- /client
- /orders
See examples/filtering/by-path/cfg.yaml for a complete example.
Filter by Tags¶
Filter operations by OpenAPI tags.
filter:
include:
tags:
- purchase
- admin
See examples/filtering/by-tag/cfg.yaml for a complete example.
Filter by Operation IDs¶
Filter specific operations by their operationId.
filter:
include:
operation-ids:
- getUser
- createUser
exclude:
operation-ids:
- deleteUser
Filter by Webhooks¶
Filter webhook entries by name. This is useful for OpenAPI 3.1 specs that define webhooks - schemas referenced by included webhooks are preserved during pruning.
filter:
include:
webhooks:
- order.created
- payment.completed
exclude:
webhooks:
- internal.debug
Webhook operations also respect tag and operation ID filters - if a webhook's operation doesn't match the tag/operation ID filter, it will be removed just like path operations.
Filter by Schema Properties¶
Filter which properties are included in generated types. This triggers transitive pruning - schemas that are only referenced by filtered-out properties will also be pruned.
filter:
include:
schema-properties:
User:
- id
- email
- name
- organization
Organization:
- id
- name
- plan
In this example:
- Only the specified properties are included in User and Organization types
- If User.address referenced an Address schema, and address is not in the include list, the Address schema will be pruned (unless referenced elsewhere)
See examples/filtering/by-property/cfg.yaml for a detailed example with comments explaining transitive pruning.
Filter by Extensions¶
Filter operations or schemas by custom OpenAPI extensions.
filter:
include:
extensions:
- x-audit-log
- x-display-name
exclude:
extensions:
- x-internal
- x-deprecated
See examples/filtering/by-extension/cfg.yaml for a complete example.
Component Pruning¶
By default, oapi-codegen prunes unused component schemas. You can control this behavior:
# Keep all schemas, even if unused
skip-prune: true
# Or use filtering to keep specific components
filter:
include:
paths:
- /users
# This will keep User schema and all schemas it references
See examples/filtering/by-components/cfg.yaml for examples of component pruning behavior.
Additional Imports¶
Add custom Go imports to generated code.
additional-imports:
- package: github.com/google/uuid
- package: github.com/shopspring/decimal
alias: dec
Error Mapping¶
Configure response types to implement the error interface. The value is a dotted path to the error message field.
error-mapping:
GetClientErrorResponse: message
UpdateClientErrorResponseJSON: arrayField[].code
Each key names an error response type: the component name when the response references a component, or <OperationId>ErrorResponse when the error schema is defined inline in the operation.
When configured, the response type will have:
Error() stringmethod - Returns the value from the specified field path- Constructor function -
NewTypeName(message string)for easy error creation
An entry that cannot be applied is skipped with a warning at generation time. This happens when:
- The key does not name an error response type. If the response references a component that is only an alias (
AliasedError: {$ref: BaseError}), map the target type (BaseError) instead. - The path does not resolve against the type's properties, or against the variants of a union (see Union Error Types). The type keeps the default
Error(), which returns"unmapped client error".
Generated Code Example¶
Given this configuration:
error-mapping:
InvalidRequestError: error.message
The generator produces:
type InvalidRequestError struct {
ErrorData *ErrorData `json:"error,omitempty"`
}
func (i InvalidRequestError) Error() string {
res0 := i.ErrorData
if res0 == nil {
return "unknown error"
}
return res0.Message
}
func NewInvalidRequestError(message string) InvalidRequestError {
return InvalidRequestError{ErrorData: &ErrorData{Message: message}}
}
The constructor is useful in server implementations for returning typed errors:
func (s *Service) CreateUser(ctx context.Context, opts *CreateUserOpts) (*CreateUserResponse, error) {
if opts.Body.Email == "" {
return nil, NewInvalidRequestError("email is required")
}
// ...
}
See examples/client/example1/cfg.yaml for a complete example.
Union Error Types¶
When the error type is a oneOf/anyOf union, or the path passes through one, the rest of the path is looked up in each variant. Error() returns the field of whichever variant was decoded, and "unknown error" for a variant that doesn't have it.
Given WidgetError defined as anyOf: [NotFound, string] and this configuration:
error-mapping:
WidgetError: message
The generator produces:
func (s WidgetError) Error() string {
res0 := s.WidgetError_AnyOf
if res0 == nil {
return "unknown error"
}
res1 := *res0
switch res2 := res1.Value().(type) {
case NotFound:
res3 := res2.Message
return res3
}
return "unknown error"
}
A union that doesn't have exactly two variants needs a discriminator so the decoded variant is known; without one, the entry is skipped with a warning. On an anyOf, the discriminator is only used when every variant is a $ref or declares its own discriminator value. No constructor is generated for union error types, because a message alone doesn't say which variant to build, so generated server handlers use the generic error response for them.
See examples/responses/error-mapping/union for a complete example.
User Templates¶
Override default code generation templates with your own.
user-templates:
client.tmpl: ./templates/my-client.tmpl
types.tmpl: ./templates/my-types.tmpl
User Context¶
Provide custom context values that can be used in templates.
user-context:
api-version: v1
company-name: "My Company"
Complete Example¶
Here's a comprehensive configuration example:
# yaml-language-server: $schema=https://raw.githubusercontent.com/doordash-oss/oapi-codegen-dd/HEAD/configuration-schema.json
package: myapi
copyright-header: "Copyright 2024 My Company. Code generated by oapi-codegen. DO NOT EDIT."
output:
use-single-file: false
directory: "./generated"
generate:
client: true
omit-description: false
default-int-type: int64
always-prefix-enum-values: true
validation:
skip: false
response: true
client:
name: "APIClient"
timeout: 30s
filter:
include:
tags:
- users
- orders
schema-properties:
User:
- id
- email
- name
error-mapping:
ErrorResponse: message
additional-imports:
- package: github.com/google/uuid
generate:
validation:
response: true
Client Settings¶
client.name¶
Type: string | Default: "Client"
Name of the generated client struct.
client:
name: "APIClient"
client.timeout¶
Type: duration | Default: 3s
Default timeout for HTTP requests.
client:
timeout: 30s