HTTP Middlewares

HTTP Middlewares

Table of contents

No heading

No headings in the article.

A typical HTTP request-response cycle goes like this:

  • You open your browser (client) or API client (e.g postman, insomnia) and type in a web address
  • The client takes this web address and builds a request
  • Through your ISP (Internet Service Provider) this request is delivered to a dedicated server
  • The server reads the request and builds a response containing the requested data
  • Through your ISP, this response is delivered back to your browser
  • Your browser renders the returned information as HTML pages

cycle.png source: request-response cycle

With this sort of structure/cycle, it is possible to include or insert other steps before the request gets to the server and before the response gets to the client. This is the concept or definition of HTTP middlewares - software entities with specific functionalities running before a request gets to the server and before a response gets to the client. Such middlewares might be used for authentication, logging, caching, request processing, compression, adding of headers and many other purposes.

midd.jpg source: middleware

The structure of middlewares allows the definition of the request body, response body as well as the the next middleware on the chain. Yes, you can have series of different middlewares chained together.

Infact, API ToolKit engineers made use of the possibilities made available by middlewarewares in their building of API ToolKit golang client, an sdk used to integrate golang web services with APIToolkit. It monitors incoming traffic, gathers the requests and sends the request to the apitoolkit servers.

It can be argued that using middlewares wil reduce the request-response cycle speed of your applications/services since they add extra layers of steps or processes to be carried out. But digging deeper into some of the functionalities of these middlewares such as caching and compression will prove otherwise. Several articles for instance, include the use of compression as one of the proven methods to speed up the request-response cycle of apps built with node/express. Overall, you have to find the right balance between the advantages provided by your chosen middlewares and the response speed you want to attain.

In this article, you will learn how to build a simple middleware with Go as well as a simple server app so that we can see the middleware in action.

This tutorial assumes you already have Go and any IDE installed.

Open your terminal, cd into the desired directory and run the following code (replace with your github account username).

go mod init github.com/<github-username>/go-server

Install gorilla-mux, a golang HTTP router:

go get -u github.com/gorilla/mux

Open your preferred IDE and create a Go file (e.g main.go). Put the following code snippets in the Go file.

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"

    "github.com/gorilla/mux"
)

The middleware won't do much, it'll print to the terminal the URL accessed and the customized response of each endpoint.

// middleware prints to the terminal the URL accessed as well as a customized response from the URLs
func middleware (next http.Handler) http.Handler {
    return http.HandlerFunc(func (res http.ResponseWriter, req *http.Request) {
        fmt.Printf("You accessed %v", req.URL)
        fmt.Println()

        rec := httptest.NewRecorder()
        next.ServeHTTP(rec, req)

        recRes := rec.Result()
        resBody, _ := ioutil.ReadAll(recRes.Body)

        fmt.Printf("Middleware added this response to: %v", string(resBody))
        fmt.Println()

        next.ServeHTTP(res, req)
    }) 
}

The following code snippet will create three basic endpoints we can use to test the created middleware.

func info(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(res, "Solving API observability and documentation via automation and AI\n")
}

func client(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(res, "The API Toolkit golang client is an open source sdk used to integrate golang web services with APIToolkit. It monitors incoming traffic, gathers the requests and sends the request to the apitoolkit servers.\n")
}

func server(res http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(res, "Definitely not source. Receives info from our open source API Toolkit client before sending to gcp pubsub.\n")
}
func routes() http.Handler {
    router := mux.NewRouter()
    // with one line of code, Gorilla makes our middleware accessible to all the created endpoints  
    router.Use(middleware)
    router.HandleFunc("/apitoolkit/info", info).Methods("GET")
    router.HandleFunc("/apitoolkit/client", client).Methods("GET")
    router.HandleFunc("/apitoolkit/server", server).Methods("GET")

    return router
}
func main() {
    srv := &http.Server{
        Addr: "127.0.0.1:8080",
        Handler: routes(),
    }

    fmt.Println("Listening on port 8080")

    err := srv.ListenAndServe()
    if err != nil {
        log.Fatal(err)
    }
}

Open your terminal, cd into the folder containing the Go app and run

go run main.go

If there are no errors, then you should see something like this on your terminal

Screenshot from 2022-01-22 19-16-44.png

To test the middleware, you can use API clients like postman, a curl request or a web browser. In this tutorial, a web browser is the preferred client since we have only GET endpoints.

Screenshot from 2022-01-22 18-14-13.png Screenshot from 2022-01-22 18-15-03.png Screenshot from 2022-01-22 18-15-33.png

From our terminal, we can see the middleware worked as expected. Screenshot from 2022-01-22 18-18-28.png

I've pushed the codes written in this article to this repo.