Go and the OpenAPI Ecosystem

Introduction
When developing a Production Backend server in Go, one of the first challenges that nearly every developer encounters is the following.
API documentation, how should I do it...?
Upon searching a little, one realizes that writing documentation conforming to the OpenAPI specification is beneficial, and naturally one begins looking for libraries that integrate with OpenAPI. However, even after making this decision, another problem remains.
There are so many OpenAPI-related libraries.. which one should I use...?
This document is a brief introduction to libraries, written for Go beginners who are experiencing this situation. It was written as of the end of 2024, and since the language ecosystem constantly evolves, we recommend consulting it while also keeping up with the latest developments.
The Strategies of Libraries Toward OpenAPI
As you may already know, OpenAPI is a specification for clearly defining and documenting REST APIs. By defining API endpoints, requests, response formats, and so on in YAML or JSON format, it greatly helps automate code generation not only for developers but also for frontend and backend code, reducing meaningless repetition and minor human errors.
To naturally integrate such OpenAPI into projects, libraries in the Go ecosystem broadly adopt the following three strategies.
1. Composing OpenAPI Specification Documents from Go Comments
One difficulty when developing APIs in accordance with OpenAPI is that the actual documentation and the code implementing it exist as separate files in entirely different locations, so it is not uncommon for the code to be updated without the documentation being updated, or for the documentation to be updated without the code being updated.
As a simple example:
- Suppose the logic for an API is modified in the file
./internal/server/user.go. - The actual documentation exists in
./openapi3.yaml, and one may accidentally forget to make changes to it. - If a Pull Request is submitted without recognizing this issue and is reviewed by colleagues,
- Since the reviewers also cannot see the changes to
./openapi3.yaml, an unfortunate situation may arise in which the API specification remains unchanged while the actual API implementation has been altered.
Writing API documentation in the form of Go comments can resolve these problems to some extent. Because the code and documentation are gathered in one place, comments can be updated together with the code when modifying it. Tools that automatically generate OpenAPI specification documents based on these comments exist.
A representative project is Swag. Swag parses Go code comments and generates documentation in the OpenAPI 2 format. Its usage is simple: one only needs to write comments in the format defined by the library above the handler function.
1// @Summary 유저 생성
2// @Description 새로운 유저를 생성합니다.
3// @Tags Users
4// @Accept json
5// @Produce json
6// @Param user body models.User true "유저 정보"
7// @Success 200 {object} models.User
8// @Failure 400 {object} models.ErrorResponse
9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11 // ...
12}
When comments are written in this way, the Swag CLI parses these comments and generates an OpenAPI 2 document. Typically, this task is performed in the CI process, and the generated OpenAPI specification document is deployed to a Git Repository, the final build artifacts, or a separate external API documentation management system, to be used for collaboration with other projects.
Advantages:
- Because the comments reside together with the code, the likelihood of the actual code and documentation diverging is reduced.
- With nothing more than comments, without separate tools or complex configuration, documentation can be written simply and freely.
- Since comments do not affect the actual API logic, it is convenient to add temporary features that are burdensome to expose as documentation.
Disadvantages:
- As the number of comment lines increases, the readability of a single code file may decline.
- It may be difficult to express all API specifications in the form of comments.
- Because documentation does not enforce the code, there is no guarantee that the OpenAPI documentation and the actual logic match.
2. Generating Go Code from OpenAPI Specification Documents
There is also an approach that places the Single Source of Truth (SSOT) not in Go code but in the documentation. This is the method of first defining the OpenAPI specification and then generating Go code based on the defined content. Since the API specification itself generates the code, it can enforce a development culture in which API design is done first, and since defining the API specification is the very first step in the development order, it has the strength of preventing, early on, the unfortunate situation of discovering missed parts after development is complete and having the entire code modified along with changes to the API specification.
Representative projects adopting this approach include oapi-codegen and OpenAPI Generator. Their usage is simple.
- Write a YAML or JSON document conforming to the OpenAPI specification,
- Run the CLI,
- Corresponding Go stub code is generated.
- Now, you only need to directly implement the detailed logic for each individual API so that this stub can be used.
The following is an example of code generated by oapi-codegen.
1// StrictServerInterface represents all server handlers.
2type StrictServerInterface interface {
3 // ...
4 // Returns all pets
5 // (GET /pets)
6 FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7 // ...
8}
With this interface as the medium, the code generated by oapi-codegen performs logic such as parsing query parameters, headers, and bodies, along with validation, and then invokes the appropriate method declared in the interface. The user only needs to implement the implementation of this interface for the API implementation work to be complete.
Advantages:
- Because the specification comes first and development proceeds from it, it is advantageous for parallel work when multiple teams collaborate.
- Code for parts previously done as repetitive, tedious work is generated automatically, so work efficiency increases while it remains advantageous for debugging.
- It is easy to guarantee that the documentation and code always match.
Disadvantages:
- If one is unfamiliar with the OpenAPI specification itself, there is a somewhat steep initial learning curve.
- Because the structure of the code that handles APIs is automatically generated by the project, it may be difficult to respond when customization is required.
Author's comment. As of October 2024, the Go code generated by OpenAPI Generator enforces not only the API logic but the entire project structure, and its rigid project structure generates code that is unsuitable for adding the various features required in an actual Production environment. For those who adopt this approach, we strongly recommend using oapi-codegen. The author uses oapi-codegen + echo + StrictServerInterface.
3. Generating OpenAPI Specification Documents from Go Code
When dozens or hundreds of people develop on the same server, an inevitable issue arises: consistency across individual APIs can break down. As an intuitive example, if the specification for more than 100 API endpoints is declared in a single OpenAPI YAML file, that file will inevitably become a monster exceeding 10,000 lines, and as new API endpoints are declared, the overall consistency of the API begins to break down, with the same models being inevitably declared redundantly, some fields being omitted, or path naming that does not follow conventions emerging.
To resolve these issues, one could assign a dedicated owner to manage the OpenAPI YAML or develop a linter to catch problems automatically during the CI process. However, by defining a Domain-specific language (DSL) in Go, one can enforce consistency so that all APIs share a uniform structure.
A representative project employing this technique is Kubernetes (built in-house without a separate library), and projects such as go-restful and goa may also be used. The following is an example of usage of goa.
1var _ = Service("user", func() {
2 Method("create", func() {
3 Payload(UserPayload)
4 Result(User)
5 HTTP(func() {
6 POST("/users")
7 Response(StatusOK)
8 })
9 })
10})
By writing compilable Go code like the above, one gains the strength that the implementation and the documentation definition for the POST /users API are completed simultaneously.
Advantages:
- Because everything is generated from code, it is easy to maintain API consistency across the entire project.
- By leveraging Go's strong type system, one can obtain a more precise and less controversial specification than when using all the features of OpenAPI 3.
Disadvantages:
- One must learn the DSL defined by each framework, and it may be difficult to apply to existing code.
- Because one must follow the rules proposed by the framework, freedom and flexibility may be reduced.
Concluding Remarks
Each method has its own advantages and disadvantages, and it is important to choose the appropriate method according to the project's requirements and the team's preferences. The most important thing is not which method is better, but to make a value judgment about what the most suitable solution is for your current situation, and to pursue high development productivity so that you can enjoy early departures from work and a satisfying work-life balance.
Although this article was written as of October 2024, the Go and OpenAPI ecosystems continue to evolve, so please keep following up on the latest developments and the changed advantages and disadvantages of each library and project, considering the time gap between when you read this and when it was written.
Have a happy Go life~ 😘