MCPcopy Index your code
hub / github.com/oapi-codegen/oapi-codegen

github.com/oapi-codegen/oapi-codegen @v2.7.1 sqlite

repository ↗ · DeepWiki ↗ · release v2.7.1 ↗
9,537 symbols 17,564 edges 514 files 5,557 documented · 58%
README

oapi-codegen

OpenSSF Best Practices

oapi-codegen is a command-line tool and library to convert OpenAPI specifications to Go code, be it server-side implementations, API clients, or simply HTTP models.

Using oapi-codegen allows you to reduce the boilerplate required to create or integrate with services based on OpenAPI 3.0, and instead focus on writing your business logic, and working on the real value-add for your organisation.

With oapi-codegen, there are a few Key Design Decisions we've made, including:

  • idiomatic Go, where possible
  • fairly simple generated code, erring on the side of duplicate code over nicely refactored code
  • supporting as much of OpenAPI 3.x as is possible, alongside Go's type system

oapi-codegen is one part of a wider ecosystem, which can be found described in further detail in the oapi-codegen organisation on GitHub.

⚠️ This README may be for the latest development version, which may contain unreleased changes. Please ensure you're looking at the README for the latest release version.

Action Required: The repository for this project has changed

As announced in May 2024, we have moved the project from the deepmap organization to our own organization, and you will need to update your import paths to pull updates past this point. You need to do a recursive search/replace from github.com/deepmap/oapi-codegen/v2 to github.com/oapi-codegen/oapi-codegen/v2.

[!IMPORTANT] oapi-codegen moved to its new home with the version tag v2.3.0.

If you are using v2.2.0 or below, please install like so:

# for the binary install
go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@v2.2.0

If you are using v2.3.0 or above, please install like so, using the new module import path:

# for the binary install
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

Install

It is recommended to follow the go tool support available from Go 1.24+ for managing the dependency of oapi-codegen alongside your core application.

To do this, you run go get -tool:

$ go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
# this will then modify your `go.mod`

From there, each invocation of oapi-codegen would be used like so:

//go:generate go tool oapi-codegen -config cfg.yaml ../../api.yaml

Alternatively, you can install it as a binary with:

$ go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
$ oapi-codegen -version

Which then means you can invoke it like so:

//go:generate oapi-codegen --config=config.yaml ../../api.yaml

Note that you can also move your tools.go into its own sub-module to reduce the impact on your top-level go.mod.

Pinning to commits

While the project does not (yet) have a defined release cadence, there may be cases where you want to pull in yet-unreleased changes to your codebase.

Therefore, you may want to pin your dependency on oapi-codegen to a given commit hash, rather than a tag.

This is officially recommended for consumers of oapi-codegen, who want features/bug fixes that haven't yet been released.

We aim to keep the default branch ready-to-release so you should be able to safely pin.

To do so, you can run:

# pin to the latest version on the default branch
$ go get github.com/oapi-codegen/oapi-codegen/v2@main
# alternatively, to a commit hash i.e. https://github.com/oapi-codegen/oapi-codegen/commit/71e916c59688a6379b5774dfe5904ec222b9a537
$ go get github.com/oapi-codegen/oapi-codegen/v2@71e916c59688a6379b5774dfe5904ec222b9a537

This will then make a change such as:

diff --git go.mod go.mod
index 44f29a4..436a780 100644
--- go.mod
+++ go.mod
@@ -2,21 +2,20 @@
-require github.com/oapi-codegen/oapi-codegen/v2 v2.1.0
+require github.com/oapi-codegen/oapi-codegen/v2 v2.1.1-0.20240331212514-80f0b978ef16

Usage

oapi-codegen is largely configured using a YAML configuration file, to simplify the number of flags that users need to remember, and to make reading the go:generate command less daunting.

For full details of what is supported, it's worth checking out the GoDoc for codegen.Configuration.

We also have a JSON Schema that can be used by IDEs/editors with the Language Server Protocol (LSP) to perform intelligent suggestions, i.e.:

# yaml-language-server: $schema=https://raw.githubusercontent.com/oapi-codegen/oapi-codegen/v2.6.0/configuration-schema.json
package: api
# ...

Note that it's recommended to pin to a specific version of the configuration schema, so it matches the version of oapi-codegen you're using. For instance, if you're using Renovate, you can have Renovate automagically update this version for you.

Backwards compatibility

Although we strive to retain backwards compatibility - as a project that's using a stable API per SemVer - there are sometimes opportunities we must take to fix a bug that could cause a breaking change for people relying upon the behaviour.

In this case, we will expose a compatibility option to restore old behaviour.

Features

At a high level, oapi-codegen supports:

  • Generating server-side boilerplate for a number of servers (docs)
  • Generating client API boilerplate (docs)
  • Generating the types (docs)
  • Splitting large OpenAPI specs across multiple packages(docs)
  • This is also known as "Import Mapping" or "external references" across our documentation / discussion in GitHub issues

What does it look like?

Below we can see a trimmed down example taken from the OpenAPI Petstore example:

// generated code

type ServerInterface interface {
    // ...
    // Returns all pets
    // (GET /pets)
    FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams)
    // ...
}

// FindPets operation middleware
func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Request) {

    var err error

    // Parameter object where we will unmarshal all parameters from the context
    var params FindPetsParams

    // ------------- Optional query parameter "tags" -------------

    err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
    if err != nil {
        siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "tags", Err: err})
        return
    }

    // ------------- Optional query parameter "limit" -------------

    err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
    if err != nil {
        siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "limit", Err: err})
        return
    }

    handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        siw.Handler.FindPets(w, r, params)
    }))

    for _, middleware := range siw.HandlerMiddlewares {
        handler = middleware(handler)
    }

    handler.ServeHTTP(w, r)
}

// HandlerWithOptions creates http.Handler with additional options
func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.Handler {
    m := options.BaseRouter

    if m == nil {
        m = http.NewServeMux()
    }
    if options.ErrorHandlerFunc == nil {
        options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
            http.Error(w, err.Error(), http.StatusBadRequest)
        }
    }

    wrapper := ServerInterfaceWrapper{
        Handler:            si,
        HandlerMiddlewares: options.Middlewares,
        ErrorHandlerFunc:   options.ErrorHandlerFunc,
    }

    m.HandleFunc("GET "+options.BaseURL+"/pets", wrapper.FindPets)

    return m
}

Then, in your own code, you implement the underlying logic for the FindPets implementation:

type PetStore struct {
    Pets   map[int64]Pet
    NextId int64
    Lock   sync.Mutex
}

// Make sure we conform to ServerInterface

var _ ServerInterface = (*PetStore)(nil)

func NewPetStore() *PetStore {
    return &PetStore{
        Pets:   make(map[int64]Pet),
        NextId: 1000,
    }
}

// FindPets implements all the handlers in the ServerInterface
func (p *PetStore) FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams) {
    p.Lock.Lock()
    defer p.Lock.Unlock()

    var result []Pet

    for _, pet := range p.Pets {
        if params.Tags != nil {
            // If we have tags,  filter pets by tag
            for _, t := range *params.Tags {
                if pet.Tag != nil && (*pet.Tag == t) {
                    result = append(result, pet)
                }
            }
        } else {
            // Add all pets if we're not filtering
            result = append(result, pet)
        }

        if params.Limit != nil {
            l := int(*params.Limit)
            if len(result) >= l {
                // We're at the limit
                break
            }
        }
    }

    w.WriteHeader(http.StatusOK)
    _ = json.NewEncoder(w).Encode(result)
}

As we can see, oapi-codegen simplifies some of the boilerplate by taking parameters out of the request and instead allows us to focus on the implementation.

You'll note that there's still a bit more marshaling of request/response data, which is further reduced by using the Strict server functionality.

When using the strict server, you'll have the following generated code:

// StrictServerInterface represents all server handlers.
type StrictServerInterface interface {
    // ...
    // Returns all pets
    // (GET /pets)
    FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
    // ...
}

func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface {
    return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
}

// FindPets operation middleware
func (sh *strictHandler) FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams) {
    var request FindPetsRequestObject

    request.Params = params

    handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) {
        return sh.ssi.FindPets(ctx, request.(FindPetsRequestObject))
    }
    for _, middleware := range sh.middlewares {
        handler = middleware(handler, "FindPets")
    }

    response, err := handler(r.Context(), w, r, request)

    if err != nil {
        sh.options.ResponseErrorHandlerFunc(w, r, err)
    } else if validResponse, ok := response.(FindPetsResponseObject); ok {
        if err := validResponse.VisitFindPetsResponse(w); err != nil {
            sh.options.ResponseErrorHandlerFunc(w, r, err)
        }
    } else if response != nil {
        sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response))
    }
}

Then, in your own code, you implement the underlying logic for the FindPets implementation:

// Make sure we conform to StrictServerInterface

var _ StrictServerInterface = (*PetStore)(nil)

func NewPetStore() *PetStore {
    return &PetStore{
        Pets:   make(map[int64]Pet),
        NextId: 1000,
    }
}

// FindPets implements all the handlers in the ServerInterface
func (p *PetStore) FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error) {
    p.Lock.Lock()
    defer p.Lock.Unlock()

    var result []Pet

    for _, pet := range p.Pets {
        if request.Params.Tags != nil {
            // If we have tags,  filter pets by tag
            for _, t := range *request.Params.Tags {
                if pet.Tag != nil && (*pet.Tag == t) {
                    result = append(result, pet)
                }
            }
        } else {
            // Add all pets if we're not filtering
            result = append(result, pet)
        }

        if request.Params.Limit != nil {
            l := int(*request.Params.Limit)
            if len(result) >= l {
                // We're at the limit
                break
            }
        }
    }

    return FindPets200JSONResponse(result), nil
}

We can see that this provides the best means to focus on the implementation of the business logic within the endpoint, rather than (un)marshalling types to and from JSON, or wrangling cookies or headers.

Key design decisions

  • Produce an interface that can be satisfied by your implementation, with reduced boilerplate
  • Bulk processing and parsing of OpenAPI document in Go
  • Resulting output is using Go's text/templates, which are user-overridable
  • Attempts to produce Idiomatic Go
  • Single-file output
  • Support multiple OpenAPI files by having a package-per-OpenAPI file
  • Support of OpenAPI 3.0
  • OpenAPI 3.1 support is awaiting upstream support However, we do have an experimental version using a different OpenAPI parser which does support 3.1 and 3.2, which you can play around with in our experimental repo
  • Note that this does not include OpenAPI 2.0 (aka Swagger)
  • Extract parameters from requests, to reduce work required by your implementation
  • Implicit additionalProperties are ignored by default (more details)
  • Prune unused types by default

Generating server-side boilerplate

oapi-codegen shines by making it fairly straightforward (note that this is a purposeful choice

Extension points exported contracts — how you extend this code

JWSValidator (Interface)
JWSValidator is used to validate JWS payloads and return a JWT if they're valid [2 implementers]
examples/authenticated-api/echo/server/jwt_authenticator.go
NameNormalizer (FuncType)
NameNormalizer is a function that takes a type name, and returns that type name converted into a different format. This
pkg/codegen/utils.go
ErrorHandler (FuncType)
ErrorHandler is called when there is an error in validation.
examples/petstore-expanded/echo-v5/middleware/middleware.go
JWSValidator (Interface)
JWSValidator is used to validate JWS payloads and return a JWT if they're valid [2 implementers]
examples/authenticated-api/stdhttp/server/jwt_authenticator.go
MultiErrorHandler (FuncType)
MultiErrorHandler is called when the OpenAPI filter returns a MultiError.
examples/petstore-expanded/echo-v5/middleware/middleware.go

Core symbols most depended-on inside this repo

String
called by 339
pkg/codegen/codegen.go
Run
called by 326
examples/streaming/stdhttp/sse/impl.go
Len
called by 62
examples/authenticated-api/echo/server/server.go
Params
called by 50
pkg/codegen/operations.go
SchemaNameToTypeName
called by 38
pkg/codegen/utils.go
SortedMapKeys
called by 32
pkg/codegen/utils.go
Resolve
called by 27
pkg/codegen/typemapping.go
writeJSON
called by 26
internal/test/parameters/gorilla/server.go

Shape

Method 4,983
Function 1,942
Struct 1,718
Interface 446
FuncType 253
TypeAlias 195

Languages

Go100%

Modules by API surface

internal/test/name_conflict_resolution/name_conflict_resolution.gen.go331 symbols
internal/test/parameters/client/gen/client.gen.go294 symbols
internal/test/parameters/echov5/client.gen.go284 symbols
internal/test/components/components.gen.go249 symbols
internal/test/strict-server/chi/server.gen.go245 symbols
internal/test/strict-server/stdhttp/server.gen.go231 symbols
internal/test/strict-server/client/client.gen.go230 symbols
internal/test/strict-server/gorilla/server.gen.go229 symbols
internal/test/strict-server/echo/server.gen.go219 symbols
internal/test/strict-server/gin/server.gen.go211 symbols
internal/test/strict-server/fiber/server.gen.go210 symbols
internal/test/strict-server/iris/server.gen.go209 symbols

Dependencies from manifests, versioned

github.com/CloudyKit/fastprinterv0.0.0-2020010918263 · 1×
github.com/CloudyKit/jet/v6v6.2.0 · 1×
github.com/Joker/jadev1.1.3 · 1×
github.com/Shopify/goreferrerv0.0.0-2022072916590 · 1×
github.com/andybalholm/brotliv1.1.0 · 1×
github.com/apapsch/go-jsonmerge/v2v2.0.0 · 1×
github.com/aymerick/douceurv0.2.0 · 1×
github.com/cloudwego/base64xv0.1.6 · 1×
github.com/davecgh/go-spewv1.1.2-0.20180830191 · 1×

For agents

$ claude mcp add oapi-codegen \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact