Generating a Go HTTP Client from OpenAPI schemas

It's possible that you're using OpenAPI specifications to describe the format of your API, and as noted in Use a (JSON) Schema for the Interface Portion of your RESTful API is something I really recommend for codifying part of your API documentation.
If you're on a Go project, you're hopefully autogenerating your structs, but may still be manually wiring in the HTTP plumbing.
One of the benefits of using a very structured format like the format in OpenAPI specifications is that you can programmatically generate the types, client and server.
Example OpenAPI specification
We'll base this on the OpenAPI specification demo from the Petstore.
Note that if you have an OpenAPI schema that uses $ref
s, we will need to bundle the OpenAPI document into a single file.
Generating the client
We can take advantage of the great oapi-codegen project, to give us a generator that we can use to produce our client implementation.
We can install it as a command-line utility by running:
# optionally, pin to a version
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen
This then allows us to use the really handy Go directive go:generate
which we embed in (any) source file in the project:
//go:generate oapi-codegen --package=main -generate=types,client -o ./petstore.gen.go https://petstore3.swagger.io/api/v3/openapi.json
This allows us to execute the oapi-codegen
when we execute go generate
on the command-line.
This then allows us to write the following code, which can utilise the generated code:
package main
//go:generate oapi-codegen --package=main -generate=client,types -o ./petstore.gen.go https://petstore3.swagger.io/api/v3/openapi.json
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
)
func main() {
c, err := NewClientWithResponses("https://petstore3.swagger.io/api/v3/")
if err != nil {
panic(err)
}
var status FindPetsByStatusParamsStatus
status = "available"
params := FindPetsByStatusParams{Status: &status}
resp, err := c.FindPetsByStatusWithResponse(context.Background(), ¶ms)
if err != nil {
panic(err)
}
pets := *resp.JSON200
if len(pets) > 0 {
fmt.Println(*pets[0].Id)
fmt.Println(pets[0].Name)
}
}