diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index cefafa4ad2..0000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Build project
-on: [ push, pull_request ]
-jobs:
- build:
- name: Build
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- # perform matrix testing to give us an earlier insight into issues with different versions of supported major versions of Go
- matrix:
- version:
- - "1.20"
- - "1.21"
- - "1.22"
- steps:
- - name: Check out source code
- uses: actions/checkout@v3
-
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: ${{ matrix.version }}
-
- - name: Test
- run: make test
- env:
- # A combination of our GitHub Actions setup, with the Go toolchain, leads to inconsistencies in calling `go env`, in particular with Go 1.21, where having (the default) `GOTOOLCHAIN=auto` results in build failures
- GOTOOLCHAIN: local
-
- - name: Build
- run: go build ./cmd/oapi-codegen
diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml
deleted file mode 100644
index a7ebeed22b..0000000000
--- a/.github/workflows/generate.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Ensure generated files are up-to-date
-on: [ push, pull_request ]
-jobs:
- build:
- name: Build
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- # perform matrix testing to give us an earlier insight into issues with different versions of supported major versions of Go
- matrix:
- version:
- - "1.20"
- - "1.21"
- - "1.22"
- steps:
- - name: Check out source code
- uses: actions/checkout@v3
-
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: ${{ matrix.version }}
-
- - name: Run `make generate`
- run: make generate
- env:
- # A combination of our GitHub Actions setup, with the Go toolchain, leads to inconsistencies in calling `go env`, in particular with Go 1.21, where having (the default) `GOTOOLCHAIN=auto` results in build failures
- GOTOOLCHAIN: local
-
- - name: Check for no untracked files
- run: git status && git diff-index --exit-code -p HEAD --
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index 991dcf2ab2..0000000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-name: Lint project
-on: [push, pull_request]
-jobs:
- build:
- name: Build
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- # perform matrix testing to give us an earlier insight into issues with different versions of supported major versions of Go
- matrix:
- version:
- - "1.20"
- - "1.21"
- - "1.22"
- steps:
- - name: Check out source code
- uses: actions/checkout@v3
-
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: ${{ matrix.version }}
-
- - name: Run `make lint-ci`
- run: make lint-ci
- env:
- # A combination of our GitHub Actions setup, with the Go toolchain, leads to inconsistencies in calling `go env`, in particular with Go 1.21, where having (the default) `GOTOOLCHAIN=auto` results in build failures
- GOTOOLCHAIN: local
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
deleted file mode 100644
index 1890eb4a5b..0000000000
--- a/.github/workflows/release-drafter.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Release Drafter
-
-on:
- push:
- branches:
- - master
- workflow_dispatch: {}
-
-permissions:
- contents: read
-
-jobs:
- update_release_draft:
- permissions:
- contents: write
- pull-requests: write
- runs-on: ubuntu-latest
- steps:
- - uses: release-drafter/release-drafter@v6
- with:
- name: next
- tag: next
- version: next
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/tidy.yml b/.github/workflows/tidy.yml
deleted file mode 100644
index 60a15c6e59..0000000000
--- a/.github/workflows/tidy.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Ensure `go mod tidy` has been run
-on: [ push, pull_request ]
-jobs:
- build:
- name: Build
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- # perform matrix testing to give us an earlier insight into issues with different versions of supported major versions of Go
- matrix:
- version:
- - "1.20"
- - "1.21"
- - "1.22"
- steps:
- - name: Check out source code
- uses: actions/checkout@v3
-
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: ${{ matrix.version }}
-
- - name: Install `tidied`
- run: go install gitlab.com/jamietanna/tidied@latest
-
- - name: Check for no untracked files
- run: make tidy-ci
- env:
- # A combination of our GitHub Actions setup, with the Go toolchain, leads to inconsistencies in calling `go env`, in particular with Go 1.21, where having (the default) `GOTOOLCHAIN=auto` results in build failures
- GOTOOLCHAIN: local
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..6f2a02c429
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,78 @@
+## Contributing
+
+If you're interested in contributing to `oapi-codegen`, the first thing we have to say is thank you! We'd like to extend our gratitude to anyone who takes the time to improve this project.
+
+`oapi-codegen` is being actively maintained, however the two people who do so are very busy, and can only set aside time for this project every once in a while, so our release cadence is slow and conservative.
+
+> [!NOTE]
+> We're actively considering what needs to change to make `oapi-codegen` more sustainable, and hope that we can share soon some options.
+
+
+```
+Generating code which others depend on, which is based on something as complex
+as OpenAPI is fraught with many edge cases, and we prefer to leave things as
+they are if there is a reasonable workaround.
+
+If you do find a case where oapi-codegen is broken, and would like to submit a PR,
+we are very grateful, and will happily look at it.
+
+Since most commits affect generated code, before sending your PR, please
+ensure that all boilerplate has been regenerated. You can do this from the top level
+of the repository by running:
+
+ make generate
+
+I realize that our code isn't entirely idiomatic with respect to comments, and
+variable naming and initialisms, especially the generated code, but I'm reluctant
+to merge PR's which change this, due to the breakage they will cause for others. If
+you rename anything under `/pkg` or change the names of variables in generated
+code, you will break other people's code. It's safe to rename internal names.
+
+
+```
+
+This guide is a starting point, and we'd absolutely **??**. We've managed to go ~4 years without a substantial guide like this, and would love to keep improving it for the best of the community.
+
+### Raising a bug
+
+If you believe **??**.
+
+This may get converted into a feature request if needed.
+
+### Asking a question
+
+We'd prefer that questions about "how do I use (this feature)" or **??** get asked using [GitHub Discussions](https://github.com/deepmap/oapi-codegen/discussions) which allow **??**
+
+### Making changes that tweak generated code
+
+If you are generating **??**.
+
+These provide a **??**, and as they are checked in to source code, allow us to **??**.
+
+Now, please note that significant changes to generated code are likley to **??**, especially in cases where there would be a breaking change issued to **??**.
+
+### Feature enhancements
+
+**Should introduce a flag if possible**
+
+**Opt-in by default?**
+
+
+### Minimal reproductions
+
+### Before you raise a PR
+
+Before you send the PR, please run the following commands locally:
+
+```sh
+make tidy
+make test
+make generate
+make lint
+```
+
+It is important to use the `make` tasks due to the way we're (ab)using the Go module system to make our **??**.
+
+These are also run in GitHub Actions, across a number of Go releases.
+
+It's recommended to raise a draft PR first, so you can get
diff --git a/README.md b/README.md
index 9e1d83d636..540885c7c0 100644
--- a/README.md
+++ b/README.md
@@ -1,189 +1,178 @@
-## OpenAPI Client and Server Code Generator
-
-⚠️ 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.
-
-This package contains a set of utilities for generating Go boilerplate code for
-services based on
-[OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
-API definitions. When working with services, it's important to have an API
-contract which servers and clients both implement to minimize the chances of
-incompatibilities. It's tedious to generate Go models which precisely correspond to
-OpenAPI specifications, so let our code generator do that work for you, so that
-you can focus on implementing the business logic for your service.
-
-We have chosen to focus on [Echo](https://github.com/labstack/echo) as
-our default HTTP routing engine, due to its speed and simplicity for the generated
-stubs. [Chi](https://github.com/go-chi/chi), [Gin](https://github.com/gin-gonic/gin),
-[gorilla/mux](https://github.com/gorilla/mux), [Fiber](https://github.com/gofiber/fiber),
-[Iris](https://github.com/kataras/iris), and [1.22+ net/http](https://pkg.go.dev/net/http)
-have also been added by contributors as additional routers.
-We chose Echo because the `Context` object is a mockable interface, and it allows for some advanced
-testing.
+# `oapi-codegen`
-This package tries to be too simple rather than too generic, so we've made some
-design decisions in favor of simplicity, knowing that we can't generate strongly
-typed Go code for all possible OpenAPI Schemas. If there is a way to accomplish
-something via utility code or reflection, it's probably a better approach than
-code generation, which is fragile due to the very dynamic nature of OpenAPI and
-the very static nature of Go.
+`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.
-## Contributing
+`oapi-codegen` aims to reduce some of the tedious boilerplate that can be found when building or interacting with APIs, and focusses on:
-I would like to pre-emptively extend my gratitude to anyone who takes the time
-to improve this project.
+- 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 being actively maintained, however the two people who do so
-are very busy, and can only set aside time for this project every once in a while,
-so our release cadence is slow and conservative.
+You can read more about our [Key Design Decisions](#key-design-decisions) below.
-Generating code which others depend on, which is based on something as complex
-as OpenAPI is fraught with many edge cases, and we prefer to leave things as
-they are if there is a reasonable workaround.
+⚠️ 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.
-If you do find a case where oapi-codegen is broken, and would like to submit a PR,
-we are very grateful, and will happily look at it.
+## Install
-Since most commits affect generated code, before sending your PR, please
-ensure that all boilerplate has been regenerated. You can do this from the top level
-of the repository by running:
+It is recommended to follow [the `tools.go` pattern](https://www.jvt.me/posts/2022/06/15/go-tools-dependency-management/) for managing the dependency of `oapi-codegen` alongside your core application.
- make generate
+This would give you a `tools/tools.go`:
-I realize that our code isn't entirely idiomatic with respect to comments, and
-variable naming and initialisms, especially the generated code, but I'm reluctant
-to merge PR's which change this, due to the breakage they will cause for others. If
-you rename anything under `/pkg` or change the names of variables in generated
-code, you will break other people's code. It's safe to rename internal names.
+```go
+//go:build tools
+// +build tools
-## Overview
+package main
-We're going to use the OpenAPI example of the
-[Expanded Petstore](https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore-expanded.yaml)
-in the descriptions below, please have a look at it.
+import (
+ _ "github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen"
+)
+```
-In order to create a Go server to serve this exact schema, you would have to
-write a lot of boilerplate code to perform all the marshaling and unmarshaling
-into objects which match the OpenAPI 3.0 definition. The code generator in this
-directory does a lot of that for you. You would run it like so:
+Then, each invocation of `oapi-codegen` would be used like so:
- go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
- oapi-codegen -package petstore petstore-expanded.yaml > petstore.gen.go
+```go
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml ../../api.yaml
+```
-Let's go through that `petstore.gen.go` file to show you everything which was
-generated.
+Alternatively, you can install it as a binary with:
-## Generated Server Boilerplate
+```sh
+$ go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
+$ oapi-codegen -version
+```
-The `/components/schemas` section in OpenAPI defines reusable objects, so Go
-types are generated for these. The Pet Store example defines `Error`, `Pet`,
-`Pets` and `NewPet`, so we do the same in Go:
+Which then means you can invoke it like so:
```go
-// Error defines model for Error.
-type Error struct {
- // Error code
- Code int32 `json:"code"`
+//go:generate go run oapi-codegen --config=config.yaml ../../api.yaml
+```
- // Error message
- Message string `json:"message"`
-}
+### Pinning to ?>
-// NewPet defines model for NewPet.
-type NewPet struct {
- // Name of the pet
- Name string `json:"name"`
+While the project does not (yet) have [a defined release cadence](https://github.com/deepmap/oapi-codegen/issues/1519), **??**.
- // Type of the pet
- Tag *string `json:"tag,omitempty"`
-}
+Therefore, you may want to pin **??**.
-// Pet defines model for Pet.
-type Pet struct {
- // Unique id of the pet
- Id int64 `json:"id"`
+This is officially **recommended**.
- // Name of the pet
- Name string `json:"name"`
+We aim to keep the default branch ready-to-release so you should be able to safely pin.
- // Type of the pet
- Tag *string `json:"tag,omitempty"`
-}
+## Usage
-// Type definition for component schema "Pets"
-type Pets []Pet
-```
+`oapi-codegen` is largely configured using a YAML configuration file, to simplify the number of flags that users need to remember, and to tune various **??**.
-It's best to define objects under `/components` field in the schema, since
-those will be turned into named Go types. If you use inline types in your
-handler definitions, we will generate inline, anonymous Go types, but those
-are more tedious to deal with since you will have to redeclare them at every
-point of use.
+For full details of what is supported, it's worth checking out [the GoDoc for `codegen.Configuration`](https://pkg.go.dev/github.com/deepmap/oapi-codegen/v2/pkg/codegen#Configuration).
-For each element in the `paths` map in OpenAPI, we will generate a Go handler
-function in an interface object. Here is the generated Go interface for our
-Echo server.
+## Features
-```go
-type ServerInterface interface {
- // (GET /pets)
- FindPets(ctx echo.Context, params FindPetsParams) error
- // (POST /pets)
- AddPet(ctx echo.Context) error
- // (DELETE /pets/{id})
- DeletePet(ctx echo.Context, id int64) error
- // (GET /pets/{id})
- FindPetById(ctx echo.Context, id int64) error
-}
+`oapi-codegen` supports:
+
+- Generating server-side boilerplate for [a number of servers] TODO ([docs](#generating-server-side-boilerplate))
+- Generating client API boilerplate ([docs](#generating-api-clients))
+- Generating the types ([docs](#generating-api-models))
+- Splitting **??** ([docs](#import-mapping))
+ - Also described as ["external refs"] or "Import Mappings" in our documentation
+
+## Key design decisions
+
+- Bulk processing and parsing of OpenAPI document in Go
+- Resulting output is using Go's `text/template`s, which are user-overridable
+- Idiomatic Go
+- Single-file output
+- Support multiple OpenAPI files by having a package-per-file
+- **??**
+- Support of OpenAPI 3.x
+ - OpenAPI 3.1 support is [awaiting upstream support](https://github.com/deepmap/oapi-codegen/issues/373)
+ - Note that this does not include OpenAPI 2.0 (aka Swagger)
+- Extract parameters from requests
+
+```
+This package tries to be too simple rather than too generic, so we've made some
+design decisions in favor of simplicity, knowing that we can't generate strongly
+typed Go code for all possible OpenAPI Schemas. If there is a way to accomplish
+something via utility code or reflection, it's probably a better approach than
+code generation, which is fragile due to the very dynamic nature of OpenAPI and
+the very static nature of Go.
```
-These are the functions which you will implement yourself in order to create
-a server conforming to the API specification. Normally, all the arguments and
-parameters are stored on the `echo.Context` in handlers, so we do the tedious
-work of unmarshaling the JSON automatically, simply passing values into
-your handlers.
+### Generating server-side boilerplate
-Notice that `FindPetById` takes a parameter `id int64`. All path arguments
-will be passed as arguments to your function, since they are mandatory.
+`oapi-codegen` shines by making it fairly straightforward (note that this is a purposeful choice of wording here - we want to avoid words like "easy") to generate the server-side boilerplate for a backend API.
-Remaining arguments can be passed in headers, query arguments or cookies. Those
-will be written to a `params` object. Look at the `FindPets` function above, it
-takes as input `FindPetsParams`, which is defined as follows:
+```
+--------
+```
+
+Now you've generated this, you need to implement **??**.
```go
-// Parameters object for FindPets
-type FindPetsParams struct {
- Tags *[]string `json:"tags,omitempty"`
- Limit *int32 `json:"limit,omitempty"`
-}
+
```
-The HTTP query parameter `limit` turns into a Go field named `Limit`. It is
-passed by pointer, since it is an optional parameter. If the parameter is
-specified, the pointer will be non-`nil`, and you can read its value.
+To provide you a fully Test Driven Development style test harness, you could use a tool such as **??** to **??**.
-If you changed the OpenAPI specification to make the parameter required, the
-`FindPetsParams` structure will contain the type by value:
+#### Supported Servers
+
+
+
+
+|
+Server
+ |
+
+generate flag to enable code generation
+ |
+
+Example usage
+ |
+
+
+
+|
+
+[Chi](https://github.com/go-chi/chi)
+
+ |
+
+chi-server
+ |
+
+
+
+Chi
```go
-type FindPetsParams struct {
- Tags *[]string `json:"tags,omitempty"`
- Limit int32 `json:"limit"`
+type PetStoreImpl struct {}
+func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
+ // Implement me
+}
+
+func SetupHandler() {
+ var myApi PetStoreImpl
+
+ r := chi.NewRouter()
+ r.Mount("/", Handler(&myApi))
}
```
-### Registering handlers
+
+ |
+
-There are a few ways of registering your http handler based on the type of server generated i.e. `-generate server` or `-generate chi-server`
+
+
-Echo
+[Echo](https://github.com/labstack/echo)
-Code generated using `-generate server`.
+ |
+
+echo-server
+ |
+
+Echo
-The usage of `Echo` is out of scope of this doc, but once you have an
-echo instance, we generate a utility function to help you associate your handlers
-with this autogenerated code. For the pet store, it looks like this:
+The usage of `Echo` is out of scope of this doc, but once you have an echo instance, we generate a utility function to help you associate your handlers with this autogenerated code. For the pet store, it looks like this:
```go
func RegisterHandlers(router codegen.EchoRouter, si ServerInterface) {
@@ -212,27 +201,31 @@ func SetupHandler() {
```
+ |
+
-Chi
+
+|
-Code generated using `-generate chi-server`.
+[Fiber](https://github.com/gofiber/fiber)
-```go
-type PetStoreImpl struct {}
-func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
- // Implement me
-}
+ |
+
+fiber-server
+ |
+
-func SetupHandler() {
- var myApi PetStoreImpl
- r := chi.NewRouter()
- r.Mount("/", Handler(&myApi))
-}
-```
+
+|
-
+[Gin](https://github.com/gin-gonic/gin)
+ |
+
+gin-server
+ |
+
Gin
Code generated using `-generate gin`.
@@ -255,211 +248,1157 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options
router.GET(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
return router
}
-```
+```
+
+```go
+import (
+ "github.com/gin-gonic/gin"
+ "github.com/deepmap/oapi-codegen/examples/petstore-expanded/gin/api"
+ middleware "github.com/oapi-codegen/gin-middleware"
+)
+
+type PetStoreImpl struct {}
+func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
+ // Implement me
+}
+
+func SetupHandler() {
+ var myApi PetStoreImpl
+
+ r := gin.Default()
+ r.Use(middleware.OapiRequestValidator(swagger))
+ r = api.RegisterHandlers(r, petStore)
+}
+```
+
+
+ |
+
+
+
+|
+
+[gorilla/mux](https://github.com/gorilla/mux)
+
+ |
+
+gorilla-server
+ |
+
+
+
+|
+
+[Iris](https://github.com/kataras/iris)
+
+ |
+
+iris-server
+ |
+
+
+
+Iris
+
+Code generated using `-generate iris`.
+
+The usage of `iris` is out of scope of this doc, but once you have an
+iris instance, we generate a utility function to help you associate your handlers
+with this autogenerated code. For the pet store, it looks like this:
+
+```go
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+
+ wrapper := ServerInterfaceWrapper{
+ Handler: si,
+ }
+
+ router.Get(options.BaseURL+"/pets", wrapper.FindPets)
+ router.Post(options.BaseURL+"/pets", wrapper.AddPet)
+ router.Delete(options.BaseURL+"/pets/:id", wrapper.DeletePet)
+ router.Get(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
+
+ router.Build()
+}
+```
+
+```go
+import (
+ "github.com/kataras/iris/v12"
+ "github.com/deepmap/oapi-codegen/examples/petstore-expanded/iris/api"
+ middleware "github.com/oapi-codegen/iris-middleware"
+)
+```
+
+The wrapper functions referenced above contain generated code which pulls
+parameters off the `Iris` request context, and unmarshals them into Go objects.
+
+You would register the generated handlers as follows:
+
+```go
+func SetupHandler() {
+ var myApi PetStoreImpl
+
+ i := iris.Default()
+ i.Use(middleware.OapiRequestValidator(swagger))
+ api.RegisterHandlers(r, &myApi)
+}
+```
+
+
+ |
+
+
+
+|
+
+[1.22+ `net/http`](https://pkg.go.dev/net/http)
+
+ |
+
+std-http-server
+ |
+
+
+
+net/http-compatible servers
+
+The following servers implement handlers that are directly compatible with the standard library:
+
+- gorilla/mux
+- Chi
+
+Therefore, these can be used as-is, for instance:
+
+```go
+type PetStoreImpl struct {}
+func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
+ // Implement me
+}
+
+func SetupHandler() {
+ var myApi PetStoreImpl
+
+ http.Handle("/", Handler(&myApi))
+}
+```
+
+
+
+Go 1.22+ net/http
+
+As of Go 1.22, enhancements have been made to the routing of the `net/http` package in the standard library.
+You can use `-generate std-http` to generate functions to help you associate your handlers with the auto-generated code.
+For the pet store, it looks like this:
+
+```go
+// 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)
+ m.HandleFunc("POST "+options.BaseURL+"/pets", wrapper.AddPet)
+ m.HandleFunc("DELETE "+options.BaseURL+"/pets/{id}", wrapper.DeletePet)
+ m.HandleFunc("GET "+options.BaseURL+"/pets/{id}", wrapper.FindPetByID)
+
+ return m
+}
+```
+
+The wrapper functions referenced above contain generated code which pulls parameters off the request and unmarshals them into Go objects.
+
+You would register the generated handlers as follows:
+
+```go
+type PetStoreImpl struct {}
+func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
+ // Implement me
+}
+
+func SetupHandler() {
+ var myApi PetStoreImpl
+
+ options := petstore.StdHTTPServerOptions{
+ BaseRouter: http.DefaultServeMux, // Or use a new ServeMux
+ }
+ petstore.HandlerWithOptions(&myApi, options)
+}
+```
+
+**Note** that if you feel like you've done everything right, but are still receiving `404 page not found` errors, make sure that you've got the `go` directive in your `go.mod` updated to:
+
+```go.mod
+go 1.22
+```
+
+
+
+ |
+
+
+
+
+#### Strict server
+
+In addition **??**.
+
+```yaml
+# TODO
+```
+
+This **??**
+
+```
+--------------------- TODO ---------
+```
+
+`oapi-codegen` also supports generating RPC inspired strict server, that will parse request bodies and encode responses.
+The main points of this code is to automate some parsing, abstract user code from server specific code,
+and also to force user code to comply with the schema.
+It supports binding of `application/json` and `application/x-www-form-urlencoded` to a struct, for `multipart` requests
+it generates a `multipart.Reader`, which can be used to either manually iterating over parts or using `runtime.BindMultipart`
+function to bind the form to a struct. All other content types are represented by a `io.Reader` interface.
+
+To form a response simply return one of the generated structs with corresponding status code and content type. For example,
+to return a status code 200 JSON response for a AddPet use the `AddPet200JSONResponse` struct which will set the correct
+Content-Type header, status code and will marshal the response data. You can also return an error, that will
+cause an `Internal Server Error` response.
+
+Short example:
+
+```go
+type PetStoreImpl struct {}
+func (*PetStoreImpl) GetPets(ctx context.Context, request GetPetsRequestObject) (GetPetsResponseObject, error) {
+ var result []Pet
+ // Implement me
+ return GetPets200JSONResponse(result), nil
+}
+```
+
+For a complete example see [`examples/petstore-expanded/strict`](examples/petstore-expanded/strict).
+
+Code is generated with a configuration flag `generate: strict-server: true` along with any other server (echo, chi, gin and gorilla are supported).
+The generated strict wrapper can then be used as an implementation for `ServerInterface`. Setup example:
+
+```go
+func SetupHandler() {
+ var myApi PetStoreImpl
+ myStrictApiHandler := api.NewStrictHandler(myApi, nil)
+ e := echo.New()
+ petstore.RegisterHandlers(e, &myStrictApiHandler)
+}
+```
+
+Strict server also has its own middlewares. It can access to both request and response structs,
+as well as raw request\response data. It can be used for logging the parsed request\response objects, transforming go errors into response structs,
+authorization, etc. Note that middlewares are server-specific.
+
+```
+--------------------- TODO ---------
+```
+
+### Generating API clients
+
+As well as generating the server-side boilerplate, `oapi-codegen` can also generate API clients.
+
+For instance, given an `api.yaml`:
+
+```yaml
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Generate models
+paths:
+ /client:
+ get:
+ operationId: getClient
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ClientType"
+ put:
+ operationId: updateClient
+ responses:
+ 400:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ code:
+ type: string
+ required:
+ - code
+components:
+ schemas:
+ ClientType:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ # NOTE that this is not generated by default because it's not referenced. If you want it, you need to use the following YAML configuration:
+ #
+ # output-options:
+ # skip-prune: true
+ Unreferenced:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+```
+
+And a `cfg.yaml`:
+
+```yaml
+package: client
+output: client.gen.go
+generate:
+ models: true
+ client: true
+```
+
+And a `generate.go`:
+
+```go
+package client
+
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
+```
+
+This would then generate:
+
+```go
+package client
+
+// ...
+
+// ClientType defines model for ClientType.
+type ClientType struct {
+ Name string `json:"name"`
+}
+
+// ...
+
+// Client which conforms to the OpenAPI3 specification for this service.
+type Client struct {
+ // The endpoint of the server conforming to this interface, with scheme,
+ // https://api.deepmap.com for example. This can contain a path relative
+ // to the server, such as https://api.deepmap.com/dev-test, and all the
+ // paths in the swagger spec will be appended to the server.
+ Server string
+
+ // Doer for performing requests, typically a *http.Client with any
+ // customized settings, such as certificate chains.
+ Client HttpRequestDoer
+
+ // A list of callbacks for modifying requests which are generated before sending over
+ // the network.
+ RequestEditors []RequestEditorFn
+}
+
+// ...
+
+// The interface specification for the client above.
+type ClientInterface interface {
+ // GetClient request
+ GetClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // UpdateClient request
+ UpdateClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+}
+
+// ...
+
+// ClientWithResponsesInterface is the interface specification for the client with responses above.
+type ClientWithResponsesInterface interface {
+ // GetClientWithResponse request
+ GetClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClientResponse, error)
+
+ // UpdateClientWithResponse request
+ UpdateClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UpdateClientResponse, error)
+}
+
+type GetClientResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *ClientType
+}
+
+// ...
+```
+
+With this generated client, it is then possible to construct and utilise the client, for instance:
+
+```go
+package client_test
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "net/http"
+
+ "github.com/deepmap/oapi-codegen/v2/examples/client"
+)
+
+func TestClient_canCall() {
+ // custom HTTP client
+ hc := http.Client{}
+
+ // with a raw http.Response
+ {
+ c, err := client.NewClient("http://localhost:1234", client.WithHTTPClient(&hc))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ resp, err := c.GetClient(context.TODO())
+ if err != nil {
+ log.Fatal(err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ log.Fatalf("Expected HTTP 200 but received %d", resp.StatusCode)
+ }
+ }
+
+ // or to get a struct with the parsed response body
+ {
+ c, err := client.NewClientWithResponses("http://localhost:1234", client.WithHTTPClient(&hc))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ resp, err := c.GetClientWithResponse(context.TODO())
+ if err != nil {
+ log.Fatal(err)
+ }
+ if resp.StatusCode() != http.StatusOK {
+ log.Fatalf("Expected HTTP 200 but received %d", resp.StatusCode())
+ }
+
+ fmt.Printf("resp.JSON200: %v\n", resp.JSON200)
+ }
+
+}
+```
+
+### Generating API models
+
+If you're looking to only generate the models for interacting with a remote service, for instance if you need to hand-roll the API client for whatever reason, you can do this as-is.
+
+For instance, given an `api.yaml`:
+
+```yaml
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Generate models
+paths:
+ /client:
+ get:
+ operationId: getClient
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ # NOTE that Client is generated here, because it's within #/components/schemas
+ $ref: "#/components/schemas/Client"
+ put:
+ operationId: updateClient
+ responses:
+ 400:
+ content:
+ application/json:
+ # NOTE that this anonymous object is /not/ generated because it's an anonymous, but would be generated if using `generate: client`
+ # See https://github.com/deepmap/oapi-codegen/issues/1512
+ schema:
+ type: object
+ properties:
+ code:
+ type: string
+ required:
+ - code
+components:
+ schemas:
+ Client:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ # NOTE that this is not generated by default because it's not referenced. If you want it, you need to use the following YAML configuration:
+ #
+ # output-options:
+ # skip-prune: true
+ Unreferenced:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+```
+
+And a `cfg.yaml`:
+
+```yaml
+package: onlymodels
+output: only-models.gen.go
+generate:
+ models: true
+```
+
+And a `generate.go`:
+
+```go
+package onlymodels
+
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
+```
+
+This would then generate:
+
+```go
+package onlymodels
+
+// Client defines model for Client.
+type Client struct {
+ Name string `json:"name"`
+}
+```
+
+If you wish to also generate the `Unreferenced` type, you would need the following `cfg.yaml`:
+
+```yaml
+package: onlymodels
+output: only-models.gen.go
+generate:
+ models: true
+output-options:
+ # NOTE that this is only required for the `Unreferenced` type
+ skip-prune: true
+```
+
+For a complete example see [`examples/petstore-expanded/only-models`](examples/petstore-expanded/only-models).
+
+### Splitting large OpenAPI specs across multiple packages (aka "Import Mapping" or "external references")
+
+
+```
+TODO
+```
+
+### Generating Nullable types
+
+It's possible that you want to be able to determine whether a field isn't sent, is sent as `null` or has a value.
+
+For instance, if you had the following OpenAPI property:
+
+```yaml
+S:
+ type: object
+ properties:
+ Field:
+ type: string
+ nullable: true
+ required: []
+```
+
+The default behaviour in `oapi-codegen` is to generate:
+
+```go
+type S struct {
+ Field *string `json:"field,omitempty"`
+}
+```
+
+However, you lose the ability to understand the three cases, as there's no way to distinguish two of the types from each other:
+
+- is this field not sent? (Can be checked with `S.Field == nil`)
+- is this field `null`? (Can be checked with `S.Field == nil`)
+- does this field have a value? (`S.Field != nil && *S.Field == "123"`)
+
+As of `oapi-codegen` [v2.1.0](https://github.com/deepmap/oapi-codegen/releases/tag/v2.1.0) it is now possible to represent this with the `nullable.Nullable` type from [our new library, oapi-codegen/nullable](https://github.com/oapi-codegen/nullable).
+
+If you configure your generator's Output Options to opt-in to this behaviour, as so:
+
+```yaml
+output-options:
+ nullable-type: true
+```
+
+You will now receive the following output:
+
+```go
+type S struct {
+ Field nullable.Nullable[string] `json:"field,omitempty"`
+}
+```
+
+### OpenAPI extensions
+
+As well as inbuilt OpenAPI, we also support the following OpenAPI extensions.
+
+####
+
+### Custom code generation
+
+It is possible to extend **??** using the templates **??**.
+
+**Note** that filenames must exactly match **??**
+
+#### Local paths
+
+#### HTTPS paths
+
+It is also possible to use HTTPS URLs.
+
+> [!WARNING]
+> Although possible, this does lead to **??**. It's recommended to vendor (copy) the OpenAPI spec into your **??**
+
+To use it, **??**
+
+```yaml
+TODO
+```
+
+> [!WARNING]
+> If using URLs that pull locations from a Git repo, such as `raw.githubusercontent.com`, it is strongly encouraged to use a tag or a raw commit hash instead of a branch like `main`. Tracking a branch can lead to unexpected API drift, and loss of the ability to reproduce a build.
+
+#### Using the Go package
+
+Alternatively, you are able to use the underlying code generation as a package, which [will be documented in the future](https://github.com/deepmap/oapi-codegen/issues/1487).
+
+### Additional Properties (`additionalProperties`)
+
+[OpenAPI Schemas](https://spec.openapis.org/oas/v3.0.3.html#schema-object) implicitly accept `additionalProperties`, meaning that any fields provided, but not explicitly defined via properties on the schema are accepted as input, and propagated. When unspecified, OpenAPI defines that the `additionalProperties` field is assumed to be `true`.
+
+For simplicity, and to remove a fair bit of duplication and boilerplate, `oapi-codegen` decides to ignore the implicit `additionalProperties: true`, and instead requires you to specify the `additionalProperties` key to generate the boilerplate.
+
+> [!NOTE]
+> In the future [this will be possible](https://github.com/deepmap/oapi-codegen/issues/1514) to disable this functionality, and honour the implicit `additionalProperties: true`
+
+Below you can see some examples of how `additionalProperties` affects the generated code.
+
+#### Implicit `additionalProperties: true` / no `additionalProperties` set
+
+```yaml
+components:
+ schemas:
+ Thing:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ # implicit additionalProperties: true
+```
+
+Will generate:
+
+```go
+// Thing defines model for Thing.
+type Thing struct {
+ Id int `json:"id"`
+}
+
+// with no generated boilerplate nor the `AdditionalProperties` field
+```
+
+#### Explicit `additionalProperties: true`
+
+```yaml
+components:
+ schemas:
+ Thing:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ # explicit true
+ additionalProperties: true
+```
+
+Will generate:
+
+```go
+// Thing defines model for Thing.
+type Thing struct {
+ Id int `json:"id"`
+ AdditionalProperties map[string]interface{} `json:"-"`
+}
+
+// with generated boilerplate below
+```
+
+
+
+Boilerplate
+
+```go
+
+// Getter for additional properties for Thing. Returns the specified
+// element and whether it was found
+func (a Thing) Get(fieldName string) (value interface{}, found bool) {
+ if a.AdditionalProperties != nil {
+ value, found = a.AdditionalProperties[fieldName]
+ }
+ return
+}
+
+// Setter for additional properties for Thing
+func (a *Thing) Set(fieldName string, value interface{}) {
+ if a.AdditionalProperties == nil {
+ a.AdditionalProperties = make(map[string]interface{})
+ }
+ a.AdditionalProperties[fieldName] = value
+}
+
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a *Thing) UnmarshalJSON(b []byte) error {
+ object := make(map[string]json.RawMessage)
+ err := json.Unmarshal(b, &object)
+ if err != nil {
+ return err
+ }
+
+ if raw, found := object["id"]; found {
+ err = json.Unmarshal(raw, &a.Id)
+ if err != nil {
+ return fmt.Errorf("error reading 'id': %w", err)
+ }
+ delete(object, "id")
+ }
+
+ if len(object) != 0 {
+ a.AdditionalProperties = make(map[string]interface{})
+ for fieldName, fieldBuf := range object {
+ var fieldVal interface{}
+ err := json.Unmarshal(fieldBuf, &fieldVal)
+ if err != nil {
+ return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
+ }
+ a.AdditionalProperties[fieldName] = fieldVal
+ }
+ }
+ return nil
+}
+
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a Thing) MarshalJSON() ([]byte, error) {
+ var err error
+ object := make(map[string]json.RawMessage)
+
+ object["id"], err = json.Marshal(a.Id)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling 'id': %w", err)
+ }
+
+ for fieldName, field := range a.AdditionalProperties {
+ object[fieldName], err = json.Marshal(field)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
+ }
+ }
+ return json.Marshal(object)
+}
+```
+
+
+
+
+#### `additionalProperties` as `integer`s
+
+```yaml
+components:
+ schemas:
+ Thing:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ # simple type
+ additionalProperties:
+ type: integer
+```
+
+Will generate:
+
+```go
+// Thing defines model for Thing.
+type Thing struct {
+ Id int `json:"id"`
+ AdditionalProperties map[string]int `json:"-"`
+}
+
+// with generated boilerplate below
+```
+
+
+
+Boilerplate
+
+```go
+// Getter for additional properties for Thing. Returns the specified
+// element and whether it was found
+func (a Thing) Get(fieldName string) (value int, found bool) {
+ if a.AdditionalProperties != nil {
+ value, found = a.AdditionalProperties[fieldName]
+ }
+ return
+}
+
+// Setter for additional properties for Thing
+func (a *Thing) Set(fieldName string, value int) {
+ if a.AdditionalProperties == nil {
+ a.AdditionalProperties = make(map[string]int)
+ }
+ a.AdditionalProperties[fieldName] = value
+}
-```go
-import (
- "github.com/gin-gonic/gin"
- "github.com/deepmap/oapi-codegen/examples/petstore-expanded/gin/api"
- middleware "github.com/oapi-codegen/gin-middleware"
-)
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a *Thing) UnmarshalJSON(b []byte) error {
+ object := make(map[string]json.RawMessage)
+ err := json.Unmarshal(b, &object)
+ if err != nil {
+ return err
+ }
-type PetStoreImpl struct {}
-func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
- // Implement me
+ if raw, found := object["id"]; found {
+ err = json.Unmarshal(raw, &a.Id)
+ if err != nil {
+ return fmt.Errorf("error reading 'id': %w", err)
+ }
+ delete(object, "id")
+ }
+
+ if len(object) != 0 {
+ a.AdditionalProperties = make(map[string]int)
+ for fieldName, fieldBuf := range object {
+ var fieldVal int
+ err := json.Unmarshal(fieldBuf, &fieldVal)
+ if err != nil {
+ return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
+ }
+ a.AdditionalProperties[fieldName] = fieldVal
+ }
+ }
+ return nil
}
-func SetupHandler() {
- var myApi PetStoreImpl
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a Thing) MarshalJSON() ([]byte, error) {
+ var err error
+ object := make(map[string]json.RawMessage)
- r := gin.Default()
- r.Use(middleware.OapiRequestValidator(swagger))
- r = api.RegisterHandlers(r, petStore)
+ object["id"], err = json.Marshal(a.Id)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling 'id': %w", err)
+ }
+
+ for fieldName, field := range a.AdditionalProperties {
+ object[fieldName], err = json.Marshal(field)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
+ }
+ }
+ return json.Marshal(object)
}
```
-net/http
+#### `additionalProperties` with an object
+
+```yaml
+components:
+ schemas:
+ Thing:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ # object
+ additionalProperties:
+ type: object
+ properties:
+ foo:
+ type: string
+```
-[Chi](https://github.com/go-chi/chi) is 100% compatible with `net/http` allowing the following with code generated using `-generate chi-server`.
+Will generate:
```go
-type PetStoreImpl struct {}
-func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
- // Implement me
+// Thing defines model for Thing.
+type Thing struct {
+ Id int `json:"id"`
+ AdditionalProperties map[string]struct {
+ Foo *string `json:"foo,omitempty"`
+ } `json:"-"`
}
-func SetupHandler() {
- var myApi PetStoreImpl
-
- http.Handle("/", Handler(&myApi))
-}
+// with generated boilerplate below
```
-Alternatively, [Gorilla](https://github.com/gorilla/mux) is also 100% compatible with `net/http` and can be generated with `-generate gorilla`.
-
-
-
-1.22+ net/http
+
-As of Go 1.22, enhancements have been made to the routing of the `net/http` package in the standard library.
-You can use `-generate std-http` to generate functions to help you associate your handlers with the auto-generated code.
-For the pet store, it looks like this:
+Boilerplate
```go
-// HandlerWithOptions creates http.Handler with additional options
-func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.Handler {
- m := options.BaseRouter
+// Getter for additional properties for Thing. Returns the specified
+// element and whether it was found
+func (a Thing) Get(fieldName string) (value struct {
+ Foo *string `json:"foo,omitempty"`
+}, found bool) {
+ if a.AdditionalProperties != nil {
+ value, found = a.AdditionalProperties[fieldName]
+ }
+ return
+}
- if m == nil {
- m = http.NewServeMux()
+// Setter for additional properties for Thing
+func (a *Thing) Set(fieldName string, value struct {
+ Foo *string `json:"foo,omitempty"`
+}) {
+ if a.AdditionalProperties == nil {
+ a.AdditionalProperties = make(map[string]struct {
+ Foo *string `json:"foo,omitempty"`
+ })
}
- if options.ErrorHandlerFunc == nil {
- options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
- http.Error(w, err.Error(), http.StatusBadRequest)
+ a.AdditionalProperties[fieldName] = value
+}
+
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a *Thing) UnmarshalJSON(b []byte) error {
+ object := make(map[string]json.RawMessage)
+ err := json.Unmarshal(b, &object)
+ if err != nil {
+ return err
+ }
+
+ if raw, found := object["id"]; found {
+ err = json.Unmarshal(raw, &a.Id)
+ if err != nil {
+ return fmt.Errorf("error reading 'id': %w", err)
}
+ delete(object, "id")
}
- wrapper := ServerInterfaceWrapper{
- Handler: si,
- HandlerMiddlewares: options.Middlewares,
- ErrorHandlerFunc: options.ErrorHandlerFunc,
+ if len(object) != 0 {
+ a.AdditionalProperties = make(map[string]struct {
+ Foo *string `json:"foo,omitempty"`
+ })
+ for fieldName, fieldBuf := range object {
+ var fieldVal struct {
+ Foo *string `json:"foo,omitempty"`
+ }
+ err := json.Unmarshal(fieldBuf, &fieldVal)
+ if err != nil {
+ return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err)
+ }
+ a.AdditionalProperties[fieldName] = fieldVal
+ }
}
+ return nil
+}
- m.HandleFunc("GET "+options.BaseURL+"/pets", wrapper.FindPets)
- m.HandleFunc("POST "+options.BaseURL+"/pets", wrapper.AddPet)
- m.HandleFunc("DELETE "+options.BaseURL+"/pets/{id}", wrapper.DeletePet)
- m.HandleFunc("GET "+options.BaseURL+"/pets/{id}", wrapper.FindPetByID)
+// Override default JSON handling for Thing to handle AdditionalProperties
+func (a Thing) MarshalJSON() ([]byte, error) {
+ var err error
+ object := make(map[string]json.RawMessage)
- return m
+ object["id"], err = json.Marshal(a.Id)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling 'id': %w", err)
+ }
+
+ for fieldName, field := range a.AdditionalProperties {
+ object[fieldName], err = json.Marshal(field)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err)
+ }
+ }
+ return json.Marshal(object)
}
+
```
-The wrapper functions referenced above contain generated code which pulls parameters off the request and unmarshals them into Go objects.
+
-You would register the generated handlers as follows:
+## Examples
-```go
-type PetStoreImpl struct {}
-func (*PetStoreImpl) GetPets(w http.ResponseWriter, r *http.Request) {
- // Implement me
-}
+The [examples directory](examples) contains some additional cases which are useful examples for how to use `oapi-codegen`, including how you'd take the Petstore API and implement it with `oapi-codegen`.
-func SetupHandler() {
- var myApi PetStoreImpl
+You could also find some cases of how the project can be used by checking out our [internal test cases](internal/test) which are real-world usages that make up our regression tests.
- options := petstore.StdHTTPServerOptions{
- BaseRouter: http.DefaultServeMux, // Or use a new ServeMux
- }
- petstore.HandlerWithOptions(&myApi, options)
-}
-```
+### Blog posts
-**Note** that if you feel like you've done everything right, but are still receiving `404 page not found` errors, make sure that you've got the `go` directive in your `go.mod` updated to:
+We love reading posts by the community about how to use the project.
-```go.mod
-go 1.22
-```
+Here are a few we've found around the Web:
-
+- [Building a Go RESTful API with design-first OpenAPI contracts](https://www.jvt.me/posts/2022/07/12/go-openapi-server/)
-Iris
+Got one to add? Please raise a PR!
-Code generated using `-generate iris`.
+--------
+--------
+--------
+--------
+--------
+--------
+--------
-The usage of `iris` is out of scope of this doc, but once you have an
-iris instance, we generate a utility function to help you associate your handlers
-with this autogenerated code. For the pet store, it looks like this:
+## Overview
-```go
-// RegisterHandlersWithOptions creates http.Handler with additional options
-func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, options IrisServerOptions) {
+We're going to use the OpenAPI example of the
+[Expanded Petstore](https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore-expanded.yaml)
+in the descriptions below, please have a look at it.
- wrapper := ServerInterfaceWrapper{
- Handler: si,
- }
+In order to create a Go server to serve this exact schema, you would have to
+write a lot of boilerplate code to perform all the marshaling and unmarshaling
+into objects which match the OpenAPI 3.0 definition. The code generator in this
+directory does a lot of that for you. You would run it like so:
- router.Get(options.BaseURL+"/pets", wrapper.FindPets)
- router.Post(options.BaseURL+"/pets", wrapper.AddPet)
- router.Delete(options.BaseURL+"/pets/:id", wrapper.DeletePet)
- router.Get(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
+ go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
+ oapi-codegen -package petstore petstore-expanded.yaml > petstore.gen.go
- router.Build()
-}
-```
+Let's go through that `petstore.gen.go` file to show you everything which was
+generated.
+
+## Generated Server Boilerplate
+
+The `/components/schemas` section in OpenAPI defines reusable objects, so Go
+types are generated for these. The Pet Store example defines `Error`, `Pet`,
+`Pets` and `NewPet`, so we do the same in Go:
```go
-import (
- "github.com/kataras/iris/v12"
- "github.com/deepmap/oapi-codegen/examples/petstore-expanded/iris/api"
- middleware "github.com/oapi-codegen/iris-middleware"
-)
-```
+// Error defines model for Error.
+type Error struct {
+ // Error code
+ Code int32 `json:"code"`
-The wrapper functions referenced above contain generated code which pulls
-parameters off the `Iris` request context, and unmarshals them into Go objects.
+ // Error message
+ Message string `json:"message"`
+}
-You would register the generated handlers as follows:
+// NewPet defines model for NewPet.
+type NewPet struct {
+ // Name of the pet
+ Name string `json:"name"`
-```go
-func SetupHandler() {
- var myApi PetStoreImpl
+ // Type of the pet
+ Tag *string `json:"tag,omitempty"`
+}
- i := iris.Default()
- i.Use(middleware.OapiRequestValidator(swagger))
- api.RegisterHandlers(r, &myApi)
+// Pet defines model for Pet.
+type Pet struct {
+ // Unique id of the pet
+ Id int64 `json:"id"`
+
+ // Name of the pet
+ Name string `json:"name"`
+
+ // Type of the pet
+ Tag *string `json:"tag,omitempty"`
}
+
+// Type definition for component schema "Pets"
+type Pets []Pet
```
-
+It's best to define objects under `/components` field in the schema, since
+those will be turned into named Go types. If you use inline types in your
+handler definitions, we will generate inline, anonymous Go types, but those
+are more tedious to deal with since you will have to redeclare them at every
+point of use.
-#### Strict server generation
+For each element in the `paths` map in OpenAPI, we will generate a Go handler
+function in an interface object. Here is the generated Go interface for our
+Echo server.
-oapi-codegen also supports generating RPC inspired strict server, that will parse request bodies and encode responses.
-The main points of this code is to automate some parsing, abstract user code from server specific code,
-and also to force user code to comply with the schema.
-It supports binding of `application/json` and `application/x-www-form-urlencoded` to a struct, for `multipart` requests
-it generates a `multipart.Reader`, which can be used to either manually iterating over parts or using `runtime.BindMultipart`
-function to bind the form to a struct. All other content types are represented by a `io.Reader` interface.
+```go
+type ServerInterface interface {
+ // (GET /pets)
+ FindPets(ctx echo.Context, params FindPetsParams) error
+ // (POST /pets)
+ AddPet(ctx echo.Context) error
+ // (DELETE /pets/{id})
+ DeletePet(ctx echo.Context, id int64) error
+ // (GET /pets/{id})
+ FindPetById(ctx echo.Context, id int64) error
+}
+```
-To form a response simply return one of the generated structs with corresponding status code and content type. For example,
-to return a status code 200 JSON response for a AddPet use the `AddPet200JSONResponse` struct which will set the correct
-Content-Type header, status code and will marshal the response data. You can also return an error, that will
-cause an `Internal Server Error` response.
+These are the functions which you will implement yourself in order to create
+a server conforming to the API specification. Normally, all the arguments and
+parameters are stored on the `echo.Context` in handlers, so we do the tedious
+work of unmarshaling the JSON automatically, simply passing values into
+your handlers.
-Short example:
+Notice that `FindPetById` takes a parameter `id int64`. All path arguments
+will be passed as arguments to your function, since they are mandatory.
+
+Remaining arguments can be passed in headers, query arguments or cookies. Those
+will be written to a `params` object. Look at the `FindPets` function above, it
+takes as input `FindPetsParams`, which is defined as follows:
```go
-type PetStoreImpl struct {}
-func (*PetStoreImpl) GetPets(ctx context.Context, request GetPetsRequestObject) (GetPetsResponseObject, error) {
- var result []Pet
- // Implement me
- return GetPets200JSONResponse(result), nil
+// Parameters object for FindPets
+type FindPetsParams struct {
+ Tags *[]string `json:"tags,omitempty"`
+ Limit *int32 `json:"limit,omitempty"`
}
```
-For a complete example see [`examples/petstore-expanded/strict`](examples/petstore-expanded/strict).
+The HTTP query parameter `limit` turns into a Go field named `Limit`. It is
+passed by pointer, since it is an optional parameter. If the parameter is
+specified, the pointer will be non-`nil`, and you can read its value.
-Code is generated with a configuration flag `generate: strict-server: true` along with any other server (echo, chi, gin and gorilla are supported).
-The generated strict wrapper can then be used as an implementation for `ServerInterface`. Setup example:
+If you changed the OpenAPI specification to make the parameter required, the
+`FindPetsParams` structure will contain the type by value:
```go
-func SetupHandler() {
- var myApi PetStoreImpl
- myStrictApiHandler := api.NewStrictHandler(myApi, nil)
- e := echo.New()
- petstore.RegisterHandlers(e, &myStrictApiHandler)
+type FindPetsParams struct {
+ Tags *[]string `json:"tags,omitempty"`
+ Limit int32 `json:"limit"`
}
```
-Strict server also has its own middlewares. It can access to both request and response structs,
-as well as raw request\response data. It can be used for logging the parsed request\response objects, transforming go errors into response structs,
-authorization, etc. Note that middlewares are server-specific.
-
#### Additional Properties in type definitions
-[OpenAPI Schemas](https://swagger.io/specification/#schemaObject) implicitly
+[OpenAPI Schemas](https://spec.openapis.org/oas/v3.0.3.html#schema-object) implicitly
accept `additionalProperties`, meaning that any fields provided, but not explicitly
defined via properties on the schema are accepted as input, and propagated. When
unspecified, the `additionalProperties` field is assumed to be `true`.
@@ -713,17 +1652,17 @@ which help you to use the various OpenAPI 3 Authentication mechanism.
- `x-go-type-skip-optional-pointer`: specifies if the Go type should or should not be a pointer
when the property is optional. If set to true, the type will not be a pointer if the field is
optional or nullable. If set to false, the type will be a pointer.
-
+
```yaml
properties:
field:
type: string
x-go-type-skip-optional-pointer: true
```
-
+
In the example above, the `field` field will be of type `string` instead of `*string`. This is
useful when you want to handle the case of an empty string differently than a null value.
-
+
- `x-go-name`: specifies Go field name. It allows you to specify the field name for a schema, and
will override any default value. This extended property isn't supported in all parts of
OpenAPI, so please refer to the spec as to where it's allowed. Swagger validation tools will
@@ -871,7 +1810,7 @@ which help you to use the various OpenAPI 3 Authentication mechanism.
End *openapi_types.Date `json:"end,omitempty"`
}
```
-
+
## Using `oapi-codegen`
The default options for `oapi-codegen` will generate everything; client, server,
@@ -909,7 +1848,7 @@ run `oapi-codegen -generate types,server`. You could generate `types` and
`server` into separate files, but both are required for the server code.
`oapi-codegen` can filter paths base on their tags or operationId in the openapi definition.
-Use either `-include-tags`, `include-operation-ids` or `-exclude-tags`, `-exclude-operation-ids`
+Use either `-include-tags`, `include-operation-ids` or `-exclude-tags`, `-exclude-operation-ids`
followed by a comma-separated list of tags or operation ids. For instance, to generate a server
that serves all paths except those tagged with `auth` or `admin`, use the argument, `-exclude-tags="auth,admin"`.
To generate a server that only handles `admin` paths, use the argument
@@ -966,16 +1905,6 @@ This tells us that in order to resolve references generated from `some_spec.yaml
need to import `github.com/deepmap/some-package`. You may specify multiple mappings
by comma separating them in the form `key1:value1,key2:value2`.
-## What's missing or incomplete
-
-This code is still young, and not complete, since we're filling it in as we
-need it. We've not yet implemented several things:
-
-- `patternProperties` isn't yet supported and will exit with an error. Pattern
- properties were defined in JSONSchema, and the `kin-openapi` Swagger object
- knows how to parse them, but they're not part of OpenAPI 3.0, so we've left
- them out, as support is very complicated.
-
## Making changes to code generation
The code generator uses a tool to inline all the template definitions into
diff --git a/examples/authenticated-api-2/api.yaml b/examples/authenticated-api-2/api.yaml
new file mode 100644
index 0000000000..18a1e02711
--- /dev/null
+++ b/examples/authenticated-api-2/api.yaml
@@ -0,0 +1,57 @@
+openapi: 3.0.0
+paths:
+ /unauthenticated:
+ get:
+ operationId: unauthenticated
+ description: Perform an unauthenticated request
+ responses:
+ 200:
+ description:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Response'
+ /apiKey:
+ get:
+ operationId: apiKey
+ description: Perform an authenticated request, using an API Key in the `X-API-Key` header
+ security:
+ - apiKey: []
+ responses:
+ 200:
+ description:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Response'
+ /httpBasic:
+ get:
+ operationId: httpBasic
+ description: Perform an authenticated request, using HTTP Basic auth
+ security:
+ - basicAuth: []
+ responses:
+ 200:
+ description:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Response'
+
+components:
+ securitySchemes:
+ apiKey:
+ type: apiKey
+ name: X-API-Key
+ in: header
+ basicAuth:
+ type: http
+ scheme: basic
+ schemas:
+ Response:
+ type: object
+ properties:
+ message:
+ type: string
+ required:
+ - message
diff --git a/examples/authenticated-api-2/stdhttp/config.yaml b/examples/authenticated-api-2/stdhttp/config.yaml
new file mode 100644
index 0000000000..fc2ed9192c
--- /dev/null
+++ b/examples/authenticated-api-2/stdhttp/config.yaml
@@ -0,0 +1,6 @@
+package: stdhttp
+generate:
+ std-http-server: true
+ embedded-spec: true
+ models: true
+output: server.gen.go
diff --git a/examples/authenticated-api-2/stdhttp/generate.go b/examples/authenticated-api-2/stdhttp/generate.go
new file mode 100644
index 0000000000..97332326e2
--- /dev/null
+++ b/examples/authenticated-api-2/stdhttp/generate.go
@@ -0,0 +1,3 @@
+package stdhttp
+
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml ../api.yaml
diff --git a/examples/authenticated-api-2/stdhttp/impl.go b/examples/authenticated-api-2/stdhttp/impl.go
new file mode 100644
index 0000000000..e43c920d9f
--- /dev/null
+++ b/examples/authenticated-api-2/stdhttp/impl.go
@@ -0,0 +1,14 @@
+package stdhttp
+
+import "net/http"
+
+type Server struct{}
+
+// (GET /apiKey)
+func (*Server) ApiKey(w http.ResponseWriter, r *http.Request) {}
+
+// (GET /httpBasic)
+func (*Server) HttpBasic(w http.ResponseWriter, r *http.Request) {}
+
+// (GET /unauthenticated)
+func (*Server) Unauthenticated(w http.ResponseWriter, r *http.Request) {}
diff --git a/examples/authenticated-api-2/stdhttp/impl_test.go b/examples/authenticated-api-2/stdhttp/impl_test.go
new file mode 100644
index 0000000000..bf5dac141b
--- /dev/null
+++ b/examples/authenticated-api-2/stdhttp/impl_test.go
@@ -0,0 +1,43 @@
+package stdhttp
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestServer(t *testing.T) {
+ var s ServerInterface = &Server{}
+
+ t.Run("unauthenticated", func(t *testing.T) {
+ t.Run("does not require authentication", func(t *testing.T) {
+
+ r := httptest.NewRequest("GET", "/unauthenticated", nil)
+ rr := httptest.NewRecorder()
+
+ s.Unauthenticated(rr, r)
+
+ assert.Equal(t, http.StatusOK, rr.Code)
+
+ fmt.Println(io.ReadAll(rr.Body))
+ })
+ })
+
+ t.Run("apiKey", func(t *testing.T) {
+ t.Run("returns **??** when no authentication", func(t *testing.T) {
+
+ r := httptest.NewRequest("GET", "/apiKey", nil)
+ rr := httptest.NewRecorder()
+
+ s.Unauthenticated(rr, r)
+
+ assert.NotEqual(t, http.StatusOK, rr.Code)
+
+ fmt.Println(io.ReadAll(rr.Body))
+ })
+ })
+}
diff --git a/examples/authenticated-api-2/stdhttp/server.gen.go b/examples/authenticated-api-2/stdhttp/server.gen.go
new file mode 100644
index 0000000000..a2ae2de8df
--- /dev/null
+++ b/examples/authenticated-api-2/stdhttp/server.gen.go
@@ -0,0 +1,306 @@
+//go:build go1.22
+
+// Package stdhttp provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT.
+package stdhttp
+
+import (
+ "bytes"
+ "compress/gzip"
+ "context"
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+
+ "github.com/getkin/kin-openapi/openapi3"
+)
+
+const (
+ ApiKeyScopes = "apiKey.Scopes"
+ BasicAuthScopes = "basicAuth.Scopes"
+)
+
+// Response defines model for Response.
+type Response struct {
+ Message string `json:"message"`
+}
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+ // (GET /apiKey)
+ ApiKey(w http.ResponseWriter, r *http.Request)
+
+ // (GET /httpBasic)
+ HttpBasic(w http.ResponseWriter, r *http.Request)
+
+ // (GET /unauthenticated)
+ Unauthenticated(w http.ResponseWriter, r *http.Request)
+}
+
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+ Handler ServerInterface
+ HandlerMiddlewares []MiddlewareFunc
+ ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+type MiddlewareFunc func(http.Handler) http.Handler
+
+// ApiKey operation middleware
+func (siw *ServerInterfaceWrapper) ApiKey(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ ctx = context.WithValue(ctx, ApiKeyScopes, []string{})
+
+ handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ siw.Handler.ApiKey(w, r)
+ }))
+
+ for _, middleware := range siw.HandlerMiddlewares {
+ handler = middleware(handler)
+ }
+
+ handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// HttpBasic operation middleware
+func (siw *ServerInterfaceWrapper) HttpBasic(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ ctx = context.WithValue(ctx, BasicAuthScopes, []string{})
+
+ handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ siw.Handler.HttpBasic(w, r)
+ }))
+
+ for _, middleware := range siw.HandlerMiddlewares {
+ handler = middleware(handler)
+ }
+
+ handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+// Unauthenticated operation middleware
+func (siw *ServerInterfaceWrapper) Unauthenticated(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ siw.Handler.Unauthenticated(w, r)
+ }))
+
+ for _, middleware := range siw.HandlerMiddlewares {
+ handler = middleware(handler)
+ }
+
+ handler.ServeHTTP(w, r.WithContext(ctx))
+}
+
+type UnescapedCookieParamError struct {
+ ParamName string
+ Err error
+}
+
+func (e *UnescapedCookieParamError) Error() string {
+ return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
+}
+
+func (e *UnescapedCookieParamError) Unwrap() error {
+ return e.Err
+}
+
+type UnmarshalingParamError struct {
+ ParamName string
+ Err error
+}
+
+func (e *UnmarshalingParamError) Error() string {
+ return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *UnmarshalingParamError) Unwrap() error {
+ return e.Err
+}
+
+type RequiredParamError struct {
+ ParamName string
+}
+
+func (e *RequiredParamError) Error() string {
+ return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
+}
+
+type RequiredHeaderError struct {
+ ParamName string
+ Err error
+}
+
+func (e *RequiredHeaderError) Error() string {
+ return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
+}
+
+func (e *RequiredHeaderError) Unwrap() error {
+ return e.Err
+}
+
+type InvalidParamFormatError struct {
+ ParamName string
+ Err error
+}
+
+func (e *InvalidParamFormatError) Error() string {
+ return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
+}
+
+func (e *InvalidParamFormatError) Unwrap() error {
+ return e.Err
+}
+
+type TooManyValuesForParamError struct {
+ ParamName string
+ Count int
+}
+
+func (e *TooManyValuesForParamError) Error() string {
+ return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
+}
+
+// Handler creates http.Handler with routing matching OpenAPI spec.
+func Handler(si ServerInterface) http.Handler {
+ return HandlerWithOptions(si, StdHTTPServerOptions{})
+}
+
+type StdHTTPServerOptions struct {
+ BaseURL string
+ BaseRouter *http.ServeMux
+ Middlewares []MiddlewareFunc
+ ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
+// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
+func HandlerFromMux(si ServerInterface, m *http.ServeMux) http.Handler {
+ return HandlerWithOptions(si, StdHTTPServerOptions{
+ BaseRouter: m,
+ })
+}
+
+func HandlerFromMuxWithBaseURL(si ServerInterface, m *http.ServeMux, baseURL string) http.Handler {
+ return HandlerWithOptions(si, StdHTTPServerOptions{
+ BaseURL: baseURL,
+ BaseRouter: m,
+ })
+}
+
+// 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+"/apiKey", wrapper.ApiKey)
+ m.HandleFunc("GET "+options.BaseURL+"/httpBasic", wrapper.HttpBasic)
+ m.HandleFunc("GET "+options.BaseURL+"/unauthenticated", wrapper.Unauthenticated)
+
+ return m
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+ "H4sIAAAAAAAC/8RSzW7bMAx+FYHb0a2D7aZbdmrQS7B1wIAgQFWZsVXElCbRB6PQuw+UG7lrLgMGbLmI",
+ "CD+S349fwPoxeELiBPoFkh1wNKX8iil4Sih1iD5gZIelM2JKpi8NngOChsTRUQ85NxDx5+QidqAPFXhs",
+ "LkD/9IyWCzChnaLj+ZucXBab4O5xlsoRaBjQdBihATKjDP+42e53N4Ko+14ncgNPJjm7nXioMqRf/l3h",
+ "A3OALNcdnTxoms7nBnxAMsGBhs+3m9sNNBAMD4VRu1LqkeXpMNnoAjsvFPcYTz6OypAyEw9I7Kxh7JTY",
+ "gIkbNSVHvfS3+526x1k5UjygeqxqHlUVKi4bWb3rQMN2uS2eLlEUSp82G3msJ0bixbZwlrPOU/uchNcl",
+ "SKk+RjyBhg/tmnT7GnNbM845v80E9GFN43DMR2m24t6XYujf2nH38LBXZVXBXSm/q5f+l/g3n1PVP9Fv",
+ "ov7EhXcjFx+uBH9/t/qfyZbfrwAAAP//ouYjQQUEAAA=",
+}
+
+// GetSwagger returns the content of the embedded swagger specification file
+// or error if failed to decode
+func decodeSpec() ([]byte, error) {
+ zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+ if err != nil {
+ return nil, fmt.Errorf("error base64 decoding spec: %w", err)
+ }
+ zr, err := gzip.NewReader(bytes.NewReader(zipped))
+ if err != nil {
+ return nil, fmt.Errorf("error decompressing spec: %w", err)
+ }
+ var buf bytes.Buffer
+ _, err = buf.ReadFrom(zr)
+ if err != nil {
+ return nil, fmt.Errorf("error decompressing spec: %w", err)
+ }
+
+ return buf.Bytes(), nil
+}
+
+var rawSpec = decodeSpecCached()
+
+// a naive cached of a decoded swagger spec
+func decodeSpecCached() func() ([]byte, error) {
+ data, err := decodeSpec()
+ return func() ([]byte, error) {
+ return data, err
+ }
+}
+
+// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
+func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
+ res := make(map[string]func() ([]byte, error))
+ if len(pathToFile) > 0 {
+ res[pathToFile] = rawSpec
+ }
+
+ return res
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file. The external references of Swagger specification are resolved.
+// The logic of resolving external references is tightly connected to "import-mapping" feature.
+// Externally referenced files must be embedded in the corresponding golang packages.
+// Urls can be supported but this task was out of the scope.
+func GetSwagger() (swagger *openapi3.T, err error) {
+ resolvePath := PathToRawSpec("")
+
+ loader := openapi3.NewLoader()
+ loader.IsExternalRefsAllowed = true
+ loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+ pathToFile := url.String()
+ pathToFile = path.Clean(pathToFile)
+ getSpec, ok := resolvePath[pathToFile]
+ if !ok {
+ err1 := fmt.Errorf("path not found: %s", pathToFile)
+ return nil, err1
+ }
+ return getSpec()
+ }
+ var specData []byte
+ specData, err = rawSpec()
+ if err != nil {
+ return
+ }
+ swagger, err = loader.LoadFromData(specData)
+ if err != nil {
+ return
+ }
+ return
+}
diff --git a/examples/client/api.yaml b/examples/client/api.yaml
new file mode 100644
index 0000000000..5945b63dd7
--- /dev/null
+++ b/examples/client/api.yaml
@@ -0,0 +1,47 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Generate models
+paths:
+ /client:
+ get:
+ operationId: getClient
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/ClientType"
+ put:
+ operationId: updateClient
+ responses:
+ 400:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ code:
+ type: string
+ required:
+ - code
+components:
+ schemas:
+ ClientType:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ # NOTE that this is not generated by default because it's not referenced. If you want it, you need to use the following YAML configuration:
+ #
+ # output-options:
+ # skip-prune: true
+ Unreferenced:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: int
diff --git a/examples/client/cfg.yaml b/examples/client/cfg.yaml
new file mode 100644
index 0000000000..be3085372a
--- /dev/null
+++ b/examples/client/cfg.yaml
@@ -0,0 +1,5 @@
+package: client
+output: client.gen.go
+generate:
+ models: true
+ client: true
diff --git a/examples/client/client.gen.go b/examples/client/client.gen.go
new file mode 100644
index 0000000000..33ed34d750
--- /dev/null
+++ b/examples/client/client.gen.go
@@ -0,0 +1,345 @@
+// Package client provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT.
+package client
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+// ClientType defines model for ClientType.
+type ClientType struct {
+ Name string `json:"name"`
+}
+
+// RequestEditorFn is the function signature for the RequestEditor callback function
+type RequestEditorFn func(ctx context.Context, req *http.Request) error
+
+// Doer performs HTTP requests.
+//
+// The standard http.Client implements this interface.
+type HttpRequestDoer interface {
+ Do(req *http.Request) (*http.Response, error)
+}
+
+// Client which conforms to the OpenAPI3 specification for this service.
+type Client struct {
+ // The endpoint of the server conforming to this interface, with scheme,
+ // https://api.deepmap.com for example. This can contain a path relative
+ // to the server, such as https://api.deepmap.com/dev-test, and all the
+ // paths in the swagger spec will be appended to the server.
+ Server string
+
+ // Doer for performing requests, typically a *http.Client with any
+ // customized settings, such as certificate chains.
+ Client HttpRequestDoer
+
+ // A list of callbacks for modifying requests which are generated before sending over
+ // the network.
+ RequestEditors []RequestEditorFn
+}
+
+// ClientOption allows setting custom parameters during construction
+type ClientOption func(*Client) error
+
+// Creates a new Client, with reasonable defaults
+func NewClient(server string, opts ...ClientOption) (*Client, error) {
+ // create a client with sane default values
+ client := Client{
+ Server: server,
+ }
+ // mutate client and add all optional params
+ for _, o := range opts {
+ if err := o(&client); err != nil {
+ return nil, err
+ }
+ }
+ // ensure the server URL always has a trailing slash
+ if !strings.HasSuffix(client.Server, "/") {
+ client.Server += "/"
+ }
+ // create httpClient, if not already present
+ if client.Client == nil {
+ client.Client = &http.Client{}
+ }
+ return &client, nil
+}
+
+// WithHTTPClient allows overriding the default Doer, which is
+// automatically created using http.Client. This is useful for tests.
+func WithHTTPClient(doer HttpRequestDoer) ClientOption {
+ return func(c *Client) error {
+ c.Client = doer
+ return nil
+ }
+}
+
+// WithRequestEditorFn allows setting up a callback function, which will be
+// called right before sending the request. This can be used to mutate the request.
+func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
+ return func(c *Client) error {
+ c.RequestEditors = append(c.RequestEditors, fn)
+ return nil
+ }
+}
+
+// The interface specification for the client above.
+type ClientInterface interface {
+ // GetClient request
+ GetClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // UpdateClient request
+ UpdateClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+}
+
+func (c *Client) GetClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewGetClientRequest(c.Server)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+func (c *Client) UpdateClient(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewUpdateClientRequest(c.Server)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+// NewGetClientRequest generates requests for GetClient
+func NewGetClientRequest(server string) (*http.Request, error) {
+ var err error
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/client")
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("GET", queryURL.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return req, nil
+}
+
+// NewUpdateClientRequest generates requests for UpdateClient
+func NewUpdateClientRequest(server string) (*http.Request, error) {
+ var err error
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/client")
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("PUT", queryURL.String(), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return req, nil
+}
+
+func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
+ for _, r := range c.RequestEditors {
+ if err := r(ctx, req); err != nil {
+ return err
+ }
+ }
+ for _, r := range additionalEditors {
+ if err := r(ctx, req); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ClientWithResponses builds on ClientInterface to offer response payloads
+type ClientWithResponses struct {
+ ClientInterface
+}
+
+// NewClientWithResponses creates a new ClientWithResponses, which wraps
+// Client with return type handling
+func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
+ client, err := NewClient(server, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return &ClientWithResponses{client}, nil
+}
+
+// WithBaseURL overrides the baseURL.
+func WithBaseURL(baseURL string) ClientOption {
+ return func(c *Client) error {
+ newBaseURL, err := url.Parse(baseURL)
+ if err != nil {
+ return err
+ }
+ c.Server = newBaseURL.String()
+ return nil
+ }
+}
+
+// ClientWithResponsesInterface is the interface specification for the client with responses above.
+type ClientWithResponsesInterface interface {
+ // GetClientWithResponse request
+ GetClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClientResponse, error)
+
+ // UpdateClientWithResponse request
+ UpdateClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UpdateClientResponse, error)
+}
+
+type GetClientResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *ClientType
+}
+
+// Status returns HTTPResponse.Status
+func (r GetClientResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r GetClientResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type UpdateClientResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON400 *struct {
+ Code string `json:"code"`
+ }
+}
+
+// Status returns HTTPResponse.Status
+func (r UpdateClientResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r UpdateClientResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+// GetClientWithResponse request returning *GetClientResponse
+func (c *ClientWithResponses) GetClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetClientResponse, error) {
+ rsp, err := c.GetClient(ctx, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseGetClientResponse(rsp)
+}
+
+// UpdateClientWithResponse request returning *UpdateClientResponse
+func (c *ClientWithResponses) UpdateClientWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UpdateClientResponse, error) {
+ rsp, err := c.UpdateClient(ctx, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseUpdateClientResponse(rsp)
+}
+
+// ParseGetClientResponse parses an HTTP response from a GetClientWithResponse call
+func ParseGetClientResponse(rsp *http.Response) (*GetClientResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &GetClientResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest ClientType
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ }
+
+ return response, nil
+}
+
+// ParseUpdateClientResponse parses an HTTP response from a UpdateClientWithResponse call
+func ParseUpdateClientResponse(rsp *http.Response) (*UpdateClientResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &UpdateClientResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
+ var dest struct {
+ Code string `json:"code"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON400 = &dest
+
+ }
+
+ return response, nil
+}
diff --git a/examples/client/doc.go b/examples/client/doc.go
new file mode 100644
index 0000000000..3bf610b9ef
--- /dev/null
+++ b/examples/client/doc.go
@@ -0,0 +1,3 @@
+package client
+
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
diff --git a/examples/only-models/api.yaml b/examples/only-models/api.yaml
new file mode 100644
index 0000000000..4e3d820663
--- /dev/null
+++ b/examples/only-models/api.yaml
@@ -0,0 +1,50 @@
+openapi: "3.0.0"
+info:
+ version: 1.0.0
+ title: Generate models
+paths:
+ /client:
+ get:
+ operationId: getClient
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ # NOTE that Client is generated here, because it's within #/components/schemas
+ $ref: "#/components/schemas/Client"
+ put:
+ operationId: updateClient
+ responses:
+ 400:
+ content:
+ application/json:
+ # NOTE that this anonymous object is /not/ generated because it's an anonymous, but would be generated if using `generate: client`
+ # See https://github.com/deepmap/oapi-codegen/issues/1512
+ schema:
+ type: object
+ properties:
+ code:
+ type: string
+ required:
+ - code
+components:
+ schemas:
+ Client:
+ type: object
+ required:
+ - name
+ properties:
+ name:
+ type: string
+ # NOTE that this is not generated by default because it's not referenced. If you want it, you need to use the following YAML configuration:
+ #
+ # output-options:
+ # skip-prune: true
+ Unreferenced:
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
diff --git a/examples/only-models/cfg.yaml b/examples/only-models/cfg.yaml
new file mode 100644
index 0000000000..1e1228b9f6
--- /dev/null
+++ b/examples/only-models/cfg.yaml
@@ -0,0 +1,7 @@
+package: onlymodels
+output: only-models.gen.go
+generate:
+ models: true
+output-options:
+ # NOTE that this is only required for the `Unreferenced` type
+ skip-prune: true
diff --git a/examples/only-models/doc.go b/examples/only-models/doc.go
new file mode 100644
index 0000000000..a94b7f2271
--- /dev/null
+++ b/examples/only-models/doc.go
@@ -0,0 +1,3 @@
+package onlymodels
+
+//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen -config cfg.yaml api.yaml
diff --git a/examples/only-models/only-models.gen.go b/examples/only-models/only-models.gen.go
new file mode 100644
index 0000000000..412e82a10f
--- /dev/null
+++ b/examples/only-models/only-models.gen.go
@@ -0,0 +1,14 @@
+// Package onlymodels provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT.
+package onlymodels
+
+// Client defines model for Client.
+type Client struct {
+ Name string `json:"name"`
+}
+
+// Unreferenced defines model for Unreferenced.
+type Unreferenced struct {
+ Id int `json:"id"`
+}