Abstracting web libraries in Go applications

Elton Minetto
Inside PicPay
Published in
3 min readAug 3, 2022

Abstracting web libraries in Go applications

In 2015, after more than a decade of using PHP as my primary programming language, I started developing projects in Go. One of the first impacts I had on this migration was the absence of web frameworks like Zend Framework, Symfony, or Laravel.

After some studies, I understood that the approach adopted by the Go community is different. Instead of adopting complex frameworks, developers are keen to follow language proverbs, such as Clear is better than clever. To achieve this, among other things, people make extensive use of the language’s great standard library.

Go has a library formed by several handy packages, and one of them is http, which delivers functionality and defines standard interfaces for implementing HTTP clients and servers. Based on these interfaces, several libraries have emerged that facilitate development. Such as:

This range of options gives teams freedom but can be problematic in large companies where each project can use a different library. To solve this problem at PicPay, we have developed a library that embraces the standard library’s interfaces and guarantees compatibility without limiting the teams in choosing one.

I’ll show you some code snippets in this article to demonstrate the concept.

Creating the abstraction

Given the following example project:

Inside the internal/api/package, I created the API. Go file:

The Start function receives the port that the HTTP server will listen on, some implementation of the http.Handler interface, and a list of parameters. We will use this parameter list to configure read and write timeouts. If the person does not pass these settings, the package will use the default timeout of 30 seconds. With this information received, the function can start an HTTP server and add the configuration of Graceful shutdown, which is a good practice.

Implementing the handlers

The next step is to implement the http.Handler interface using one of the libraries. For example, let’s implement it first using the Gin lib. I created the internal/http/gin package and the book.go file inside it:

In our application’s main function, we can now use both implementations to start the HTTP server and respond to user requests.

The content of the cmd/api/main.go file is:

If any team wants to use the Fiber lib, they need to develop an implementation of the http.Handler interface, as in the file internal/http/fiber/book.go:

And the only change needed in main.go will be to replace the handlers initialization from h := gin.Handlers(s) to h := fiber.Handlers(s) and everything else will work.

Adding global functionality

Another advantage is that we can now use the Start function of the api package to add “global” functionality to our application using middlewares.

In the file internal/api/api.go, I made two changes. The first was the creation of a new function:

And the second change was in the Start function to use this middleware. To make managing middleware easier, I used a library called Negroni, which makes the code more organized. The snippet I changed looks like this:

This approach is handy if you want to add features such as metrics collection, access control, and rate limits.

The final tip I would like to reinforce is that whenever you’re developing applications in Go, stay as loyal as possible to the interfaces provided by the language’s standard library. This way, you will ensure more compatibility with versions and less dependency on external libraries and frameworks.

--

--

Elton Minetto
Inside PicPay

Teacher, speaker, Principal Software Engineer @ PicPay. https://eltonminetto.dev. Google Developer Expert in Go