Golang Server code generation from OpenAPI Specification.

vignesh dharuman
SellerApp
Published in
6 min readJun 26, 2021

Hey learners, tried documenting my learning on OpenAPI and hope it remains a useful read for your time. Lets get started.

What is OpenAPI Specification?

OpenAPI documentation gives the definition as,

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

OpenAPI Specification is essentially a way or format to describe all your API endpoints along with their operations (i.e., GET, POST, PUT, etc) and all the parameters involved in the query, request/response payload, header, cookie, etc, of each endpoint. It is kind of a document that others can look up to get an idea of what resources are available in our server and how to access them.

OpenAPI vs(?) Swagger:

Well it is not a versus scenario, they both compliment each other. The format or specification of how we should describe our endpoints is called the OpenAPI specification. Once we have our specification ready, we have a lot of useful tools built around the specification like Swagger UI, Swagger Editor, Swagger Codegen, etc,. These are the tools we mostly use for the generation of documentation, client SDK’s and server stubs from the specification that are used with our code, hence the names OpenAPI and Swagger are interchangeably used. In fact, until recently before the release of OpenAPI 3.0 where the official name OpenAPI specification was given, it was earlier called as Swagger specification.

Well, hope the above quick explanation helped us remove “OpenAPI” from our list of jargon words. Now lets get to some hands-on.

Let us now write an OAS file (OpenAPI Specification file), a .yml file containing the OpenAPI specification for our service. As we write this file, we can understand how to define an API spec for our service. Below is the complete specification for our todo app

The top level components of our specification are as follows,
1. openapi : specifies the version of the OAS.
2. info : specifies few general details of our specification like title, description, version(the version here defines the versioning of our specification, which mean when we make changes to our endpoints or params, this version can be updated)
3. servers : specifies the list of our domains, we can have one for staging, another for production, etc,. All the endpoints we define in our specification will be relative to this server url. (/todos endpoint will be interpreted as http://localhost:3000/todos)
4. components: specifies all our data models under schemas section. We also define any of the OpenAPI supported auth mechanisms if we intend to use in our application
5. paths: specifies all the endpoints of our application along with allowed http methods under each endpoint

In the above structure, openapi_version, info and servers are straight forward, let you dig a bit deeper into components and paths and understand whats going on.

Components:
As mentioned earlier, components is the section under which we will define our data models. A corresponding golang struct will be created for each of the object we define under the schemas section here. In our specification file, we have defined two data models for Todo and Error. We declare their types as object and define all the properties that the objects will have. When defining the properties OAS follows a much modular approach by having both type and format attributes. For example, we can have the type of a property as integer and the format for it can be int32, int64, uint, etc,. We can also specify which of the properties are always required.

Paths:
This section defines all our endpoints with the list of methods/operations under each endpoint. Let us consider the /todos endpoint in our specification and explore it. We are defining two methods under our /todos endpoint, POST and GET. The POST operation is creating a todo function and GET operation is retrieving the list of todos function. For each operation we can define the following details,
* operationId: specifies the function name
* parameters: specifies the parameters that are expected by this function. It can be a url param or query param.
* requestBody: specifies if we except any request payload to execute this operation. We can specify the content type and the expected data model and also if its required or optional.
* responses: specifies the list of possible responses returned by the operation.

With that we have written our OpenAPI specification for our todo application. After this comes our fruitful step of generating server code from the specification. (we are only going to explore the server code generation, but we can do lost more stuffs with this specification like client code generation, documentation generation, server stubs, etc.)

There are many tools available that does the server code generation work for us, being golang specific we are going to use the tool called oapi-codegen.
oapi-codegen support both echo(default) and chi routing engines. Below commands generates the server code for our todo app from our specification,

oapi-codegen -generate types -o openapi_types.gen.go -package main todo.yml
oapi-codegen -generate chi-server -o openapi_server.gen.go -package main todo.yml

We should now have two generated go files in the current directory. The first command generates corresponding golang structs for all the schemas defined in our specification. The second command generates our server interface with all the required middleware to do the marshalling and unmarshalling of the data model that we have defined for each operation. Though we don’t need to make any changes to these files, let us go over them to get an idea of whats generated and how to include them in our app.

As we can see above two structs are created equivalent to the schema’s we defined in our specification. We also get the proper json tags that helps with marshalling and unmarshalling. Other types describe the parameters that are used in different operations.

Next lets have a look into the server file generated.

First important generated piece is the ServerInterface. This declares all the operations we defined in our specification. We should have a type implementing all these methods to use the generated code(this will be clear once we go over the other parts of this file).

Next useful piece in the generated code is the middleware to perform the marshalling and unmarshalling of data to and from request object. The above snippet is the middleware code generated for GetTodos operation. It does all the work on fetching the required parameters from request. Also note that after getting the request params, at line 36 it invokes the GetTodos of ServerInterfaceWrapper.Handler ( which will be our implementation of the GetTodos function from our own type satisfying the ServerInterface).

Next part is the code implementing our routing rules. This method takes in the ServerInterface(which will be our own type satisfying the interface). Hence any call to GET /todos endpoint will invoke the GetTodos middleware generated and from there our own implementation of GetTodos operation is invoked as explained in above section.

The above snippet provides the way of integrating our server logic(our own type implementing the methods of ServerInterface) with the generated server logic. There are multiple options generated in the file on how we can register our handler. Thats a lot of boilerplate code to write by ourself, thanks to the effort from communities behind OAS and oapi-codegen. With all these in place we can only concentrate on our business logic.

Finally comes the way of how we integrate our business logic with the generated code(creating our own type satisfying the ServerInterface and registering it with the generated code).

In the above code, we create a type TodoServer which implements all the methods declared by the ServerInterface. This is where all our business logics for corresponding operations goes. At line 26, we register our TodoServer type with the generated code using the generated Handler function.

That’s all I have for this article. Thanks you so much for your time reading this and hope you learnt something from this.

The complete code to play around can be found at github.

Refer to OpenAPI documentation for further reading.

Happy learning and ecstatic sharing.

--

--