RESTful API Documentation with Go and chi docgen Package
An important chore that gets neglected by developers is writing documentation for their RESTful APIs. Often, this task ends up being assigned lower priority than other tasks, such as building a new feature or modifying an existing feature. Although it delivers no immediate tangible value to end users like features, documentation produces intangible value for developers and their companies. By having detailed information about a RESTful API's endpoints, developers can quickly know how to obtain authorization for protected endpoints, access and interact with certain resources, format the data that's required in a request's body, etc. Ultimately, the value in documentation comes from increased developer productivity and saved development time. The more developers the documentation serves, the more value the documentation produces. Fortunately, you don't have to spend any time or effort to manually build and maintain documentation from scratch. There are open source tools like Swagger that automate the process of designing and generating RESTful API documentation for developers. These tools: Best of all, most of these tools only need, as input, the source code of the RESTful API or a JSON representation of the RESTful API that's compliant with the OpenAPI Specification . Or, these tools can get fed other representations of the RESTful API that are based on alternative API specifications like RAML ( R ESTful A PI M odeling L anguage). After generating the documentation, all that's left is to host and share the documentation. If you built your RESTful API with the Go chi library, then you can automatically generate routing documentation with one of its optional subpackages: docgen . Below, I'm going to show you how to leverage docgen to: To get started, clone the following repository to your local machine: This repository contains source code for a simple RESTful API built with Go and chi . The router uses a middleware stack that consists of the following middlewares: Additionally, render.SetContentType(render.ContentTypeJSON) forces the Content-Type of responses to be application/json . Most of these middlewares are ported from Goji , a minimalistic web framework for Go, and they are built with native Go packages. The router defines several endpoints for a resource that represents posts: To start up the RESTful API, first install the project's dependencies. Then, compile and execute the code. docgen lets you print a list of available routes to STDOUT at runtime, like so: This gives an overview of the resources that can be accessed from this RESTful API. To log routing documentation to STDOUT, first add the github.com/go-chi/docgen dependency to the list of imported packages and dependencies in main.go , like so: Note #1 : If you encounter the error message cannot use r (variable of type *"github.com/go-chi/chi".Mux) as type "github.com/go-chi/chi/v5".Routes in argument to docgen.JSONRoutesDoc: , then you should change the imported dependency from github.com/go-chi/chi to "github.com/go-chi/chi/v5 . Note #2 : If you encounter the error message http: panic serving 127.0.0.1:50114: runtime error: slice bounds out of range [-1:] , then you should change the imported dependency from github.com/go-chi/chi/middleware to github.com/go-chi/chi/v5/middleware . Then, inside of the main() function, call the method docgen.PrintRoutes() after mounting the sub-router defined on the postsResource struct to the main router, like so: Stop the RESTful API service and re-run the following commands to install the newly added dependency ( github.com/go-chi/docgen ) and restart the RESTful API service: Now, the RESTful API's routes get logged to STDOUT. Since documentation should be shared with other developers, let's output the routing information to a pre-formatted Markdown document. Using the docgen.MarkdownRoutesDoc() method, docgen crawls the router to build a route tree. For each route and subroute, docgen collects: Then, using this tree, docgen structures and outputs the Markdown as a string. docgen will link each middleware and handler function to its implementation in the source code (the file and line where the implementation can be located). The docgen.MarkdownRoutesDoc() method accepts two arguments: Note : Any fields omitted from the struct are zero-valued. Unlike logging the route documentation to STDOUT, the Markdown should not be generated every time the RESTful API service starts up, especially if you want to write the stringified Markdown to a separate file. Instead, let's generate the Markdown only when the user passes a specific flag to the go run command. First, let's add the flag and errors packages to the list of imported packages and dependencies in main.go , like so: Just before the main() function, declare a string flag docs with a default value of an empty string (second argument) and a short description (third argument). Note : This flag will support three values: markdown , json and raml . -docs=markdown will output the routing information to a pre-formatted Markdown document, while -docs=json and -docs=raml will output JSON and RAML representations of the RESTful API respectively. We will implement the json and raml flag values in the next section of this tutorial. At the start of the main() function body, call the flag.Parse() method to parse the command-line flags into any flags defined before the main() function. At the end of the main() function body, after calling the docgen.PrintRoutes() method, check if the docs flag has been set to markdown . If so, then call the docgen.MarkdownRoutesDoc() method to generate the Markdown and write it to a routes.md file, like so: Note #1 : f, err := os.Create("routes.md") does not follow the same pattern as the other conditional statements (of having a declaration precede conditionals). Otherwise, f.Close() would cause the error undefined: f since f would only be scoped to its corresponding conditional statement's branches. For the statement to follow the same pattern as the other conditional statements, yo