Skip to content

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

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:

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:

  1. Error() string method - Returns the value from the specified field path
  2. 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