Generating a Go HTTP Client from OpenAPI schemas

Featured image for sharing metadata for article

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 $refs, 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.

Update 2023-12-30: Since writing this post, I've become a maintainer on oapi-codegen.

I'd recommend you manage the install of the oapi-codegen CLI via a tools.go, but alternatively you could use go install:

go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest
# optionally, pin to a version
go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@v2.0.0

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 go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml https://petstore3.swagger.io/api/v3/openapi.json
// alternatively, if you've not used a `tools.go`:
//go:generate oapi-codegen --config=config.yaml https://petstore3.swagger.io/api/v3/openapi.json

This requires we have the following config.yaml:

package: main
generate:
  client: true
  models: true
output: petstore.gen.go

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 go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml https://petstore3.swagger.io/api/v3/openapi.json

import (
	"context"
	"fmt"
)

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(), &params)
	if err != nil {
		panic(err)
	}
	pets := *resp.JSON200
	if len(pets) > 0 {
		if pets[0].Id != nil {
			fmt.Println(*pets[0].Id)
		}
		fmt.Println(pets[0].Name)
	}
}

This allows us to run:

$ go generate
$ go mod tidy
# then, the resulting output is
$ go run .
4
Dog 1

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #go #openapi.

Also on:

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.