oapi-codegen: one config file decides whether you get models, a client or a server
Generate Go client and server boilerplate from OpenAPI 3 specifications
At a glance
- What is it?
- oapi-codegen turns an OpenAPI 3.0 or 3.1 specification into Go models, clients or server stubs, and most of what goes wrong with it comes down to how the tool was installed and what the config file asserts. This walks the import path move, the two install routes, import mapping, overlays, and the dependency that breaks builds when it moves.
- Who is it for?
- oapi-codegen suits teams that already own an OpenAPI document and want typed Go on both sides of it, and who can pin a generator version so regeneration stays reproducible. It does not suit a project that wants a zero-dependency client, since the generator itself pulls kin-openapi into your module graph, and the code it writes has its own, lower Go requirement that is separate from the 1.26+ needed to build the tool.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- oapi-codegen/oapi-codegen8,664 ★ · +76
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| oapi-codegen/oapi-codegen | 8.6k | 8.7k |
76 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The old Deepmap import path still installs, and stops receiving fixes
The project moved out of the Deepmap organization into its own organization, announced in May 2024, and the move landed with the version tag v2.3.0. Pulling anything past that point means updating import paths: a recursive search and replace from `github.com/deepmap/oapi-codegen/v2` to `github.com/oapi-codegen/oapi-codegen/v2`. The two supported install lines are kept side by side.
# for the binary install
go install github.com/deepmap/oapi-codegen/v2/cmd/[email protected]# for the binary install
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latestNothing errors out if you stay on the old path, and that is the trap. v2.2.0 and below install cleanly and keep working, so a module that never did the replace has no visible failure and simply receives no updates. Two of the three most recent releases are security work: v2.8.1 is titled security fixes for codegen and at runtime, and v2.8.2 security fixes for code generation. A project pinned to the deepmap path sits below both and cannot get them without the replace. The two paths are also separate Go module paths.
go get -tool edits your go.mod, go install edits your PATH
Building and installing oapi-codegen requires Go 1.26 or later, while the code it generates has its own, lower requirements. That split is the first thing to get right in CI: an old build image can compile generated code and still be unable to build the generator.
Two installation routes are offered and they leave different traces. The recommended one uses `go tool` support so the generator travels as a managed dependency of your own module.
$ go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
# this will then modify your `go.mod`Generation then runs through a directive rather than a shell command.
//go:generate go tool oapi-codegen -config cfg.yaml ../../api.yamlThe alternative installs a binary and leaves your module files untouched. The cost of that convenience is reproducibility: the binary lives on a PATH, so two developers on the same commit can run different generator versions and produce different files. With `go get -tool` your go.mod pins the version for everyone, and the cost is that you take on upgrades to the generator as dependency changes. Pick one and make it the only path your build uses.
A config file, not a flag soup, decides what gets generated
The same specification can yield three different outputs: HTTP models only, an API client, or server-side boilerplate. Which one you get is a config file decision, and the repository ships `configuration-schema.json` at its root so editors can validate what you write. Beyond the three modes, the documented surface includes server URLs for clients, a strict server option, a list of supported servers, nullable type generation, OpenAPI extension handling, several shapes of `additionalProperties`, a switch to skip the optional pointer globally, and controls for renaming generated types.
Custom code generation closes the loop: you can inject your own code through local paths, HTTPS paths, an inline template, or the Go package directly.
Two consequences follow. The first is that your config becomes an API surface of its own, one that has to be reviewed when the spec changes and that has to be kept compatible with a generator version. The second is that the documentation has a section on duplicate types generated for clients' response object types, worth reading before you generate a client.
Import mapping turns one big spec into several packages
Large specifications do not have to become one enormous package. Import mapping, described as external references in the documentation, supports two arrangements: a single package holding several specifications, and multiple packages with one specification each. The repository carries an `examples/import-mapping` directory.
The mechanics that bite are type names. When one specification is split across packages, shared types have to be resolved to their package, and two schemas with the same name in different specifications cannot both land unaliased in one package. The repository also carries an `examples/clienttypenameclash` example, and the documentation covers changing the names of generated types.
The practical shift in effort is worth stating plainly. One spec in one package means the generator resolves names for you and you never think about it. A spec large enough to justify splitting means every cross-package reference, every duplicate name and every import becomes something you maintain by hand in configuration, and a spec that gains two new schemas late in development can force a config edit. Only split when the package size is actually hurting you, and re-run generation immediately after each split to see whether the new boundary holds.
An overlay is how you patch a spec you cannot edit
Not every specification belongs to the team running the generator, and a vendor document with a mistake in it cannot simply be edited. The documentation covers modifying the input specification with an OpenAPI Overlay, and `examples/overlay` shows the shape of the setup. Related escapes exist alongside it: generating only models, changing output options, and consuming OpenAPI extensions.
The project's FAQ separately asks how to ignore parts of the spec you do not care about. Overlays are the mechanism that keeps an unowned document usable without forking it.
What that costs is a third input to your pipeline. Generation now reads a specification, an overlay and a config file, and all three have to be versioned, reviewed and passed on every invocation. Treat the overlay as code, give it the same review as the config, and delete it the day the upstream document is fixed.
Security is yours to implement, and the context scopes path is deprecated
Generated server code has no opinion about authentication. The documentation treats security in two places, on the server and on the client, and in both cases what you get is structure: where a credential belongs, where a check runs, and what the generated code expects you to return. Any enforcement you add is your own middleware or your own client code, which means the security properties of your API are not visible in the specification and not verified by regeneration.
One line item is explicitly marked as removed from the recommended path: a heading reading Deprecated, auth scopes on the request context. If your handlers currently pull scopes off the request context, that is the surface the documentation is steering you away from. The consequence for an existing codebase is that regeneration under a newer generator can change how scopes reach your handlers, so a version bump is a code review item, not a formatting change.
Release notes show how much the output can move. v2.8.0, published in July 2026, is titled OpenAPI 3.1, fewer assumptions, and a giant bug hunt. Fewer assumptions means generated code where earlier versions filled gaps on your behalf, and a bug hunt means behaviour changes where code was previously wrong. Pin a version, diff the regenerated output, and read that diff before it lands.
An upgrade to kin-openapi can break a build you never touched
The project asks its own question plainly: I have just updated my version of kin-openapi, and now I cannot build my code. That failure mode is structural rather than incidental. The module requires `github.com/getkin/kin-openapi v0.152.0`, plus testify, a JSONPath library, two YAML libraries and parts of golang.org/x. The indirect list includes jsonpointer, regexp2, a JSON schema validator and oasdiff/yaml3.
Generated types are written in terms of what those libraries parse, and the strict server and validation middleware depend on them too. In a module graph where your application resolves a different version than the one the generator was built against, the same specification can produce different types or different validation behaviour. Because generated code is usually committed, the failure surfaces in a build you did not touch, in a diff you did not write.
The mitigation available to you is the documentation's section on pinning to commits, combined with committing the generated output so any change to it is visible in review.
make generate reaches into every child module in the repo
The root Makefile is small and its structure tells you how the repository is organised. `GOBIN` points at a `bin` directory inside the checkout, and golangci-lint v2.15.0 is installed there by piping the upstream install script into a shell. Targets are `help`, `tools`, `lint`, `lint-ci`, `generate`, `test` and `tidy`.
Every real target has the same two-beat shape: run the root module explicitly, then walk child modules. Linting and testing use `git ls-files` with a glob over nested go.mod files to find each module, change into its directory and re-invoke `make` there, while `generate` runs `go generate ./...` at the root and then repeats itself in each child module. Since `examples/go.mod` exists, the example set is one of those child modules.
The consequence for a contributor is that a regeneration touches files outside your change. Regenerating after a generator bump rewrites every example in the repository, which makes review harder and makes it easy to miss a real difference in a wall of diff. Linting in CI adds `--timeout=5m` and `--output.text.path=stdout`, which tells you the run is expected to be slow enough to need a bound. The documentation also poses whether to commit generated code and whether to lint it; follow the answer you find there rather than deciding separately in each repository.
Editorial conclusion
oapi-codegen suits teams that already own an OpenAPI document and want typed Go on both sides of it, and who can pin a generator version so regeneration stays reproducible. It does not suit a project that wants a zero-dependency client, since the generator itself pulls kin-openapi into your module graph, and the code it writes has its own, lower Go requirement that is separate from the 1.26+ needed to build the tool. Before adopting it, do the recursive replace from the deepmap import path, decide between `go get -tool` and a PATH binary, write down whether generated code gets committed and linted, and diff a regeneration after bumping the generator, since release notes for v2.8.0 mention fewer assumptions and a large bug hunt that can move the output.
Frequently asked questions
what is oapi codegen
oapi-codegen is a command-line tool and library that converts OpenAPI 3.0 or 3.1 specifications into Go code: server-side implementations, API clients, or just the HTTP models. Its stated design goals are idiomatic Go where possible, generated code that leans verbose rather than complex, and supporting as much of the specification as Go's type system allows.
how to install oapi codegen
Install it as a managed tool dependency with `go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest`, which modifies your go.mod, or as a binary with `go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest`. Building and installing requires Go 1.26+, while the generated code has its own lower requirement.
how to use oapi codegen
Run it from a go:generate directive that names a config file and a spec, for example `//go:generate go tool oapi-codegen -config cfg.yaml ../../api.yaml`. The config file decides whether you generate models, a client or server boilerplate, and the repository ships a configuration-schema.json for editing it.
What is an OpenAPI generator used for?
It removes the boilerplate needed to create or integrate with services described by an OpenAPI 3.0 or 3.1 specification, so effort goes to business logic instead. In oapi-codegen the boilerplate it removes covers server stubs, clients and HTTP models.
Is OpenAPI YAML or JSON?
The invocations the repository shows point at YAML inputs, a config of cfg.yaml and a spec at ../../api.yaml. The module depends on go.yaml.in/yaml/v3 and oasdiff/yaml, along with oasdiff/yaml3, so YAML is the path its documented examples take.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/oapi-codegen-oapi-codegen)Star history chart for your README
[](https://hysenlabs.com/projects/oapi-codegen-oapi-codegen)Was this useful?