Back
Featured image of post Middleware with GO

Middleware with GO

Middleware usually sits between client site request on Front-end and the backend resource being requested. Middleware is very useful especially when we wanted to have some verifications such as the request methods, headers and JWT before we makes the API call

Create Middleware

http.Handler wrapper is a function that has one input argument and one output argument, both of type http.Handler.

Example:

func Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        //code before make the backend api call

	next.ServeHTTP(w, r) //serve and handle the http request

        //code after make the backend api call
	})
}

Now, instead of directly serving and listening our http handler like:

http.ListenAndServe(":3000", handler)

We can now wrap it within our middleware and becomes:

http.ListenAndServe(":3000", Middleware(handler))

Prerequisites:

Before everything started:

  • I will be using back the previous API example for testing our middleware.
  • I also created a simple webpage and host in PORT 5500 by using Live Server so that we can make the http request from different origin.

Client side code (Front end)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        <label>Name :</label>
        <input type="text" id="name" />
    </div>
    <div>
        <label>Age :</label>
        <input type="number" id="age" />
    </div>
    <div>
        <label>Occupation :</label>
        <input type="text" id="occupation" />
    </div>
    <button type="submit" onclick="CreateUser()">Submit</button>
    <button type="submit" onclick="GetAllUser()">Get</button>
</body>

<script>
    async function CreateUser(){
        const name = document.getElementById("name");
        const age = document.getElementById("age");
        const occupation = document.getElementById("occupation");
        const user = {};
        
        user.name = name;
        user.age = age;
        user.occupation = occupation;

        const response = await fetch("http://localhost:3000/createUser",{
                            method: "POST",
                            header: new Headers({
                                "Content-Type": "application/json"
                            }),
                            mode: "cors",
                            body: JSON.stringify(user)
                        })

        const result = await response.json()
        console.log("result",result);
    }
</script>
</html>

Middleware code

func Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            referer := r.Referer()  //return "http://127.0.0.1:5500/"
            origin := r.Header.Get("origin")  //return "http://127.0.0.1:5500"
            requestUrl := r.URL.Path  //return "/createUser"
            method := r.Method  //return "POST"

            //In case if only allow POST and GET request
            if method != "POST" && method != "GET"{
                //Response bad request if doesn't pass the condition
                w.Header().Set("Content-Type", "text/plain")
                w.WriteHeader(http.StatusBadRequest)
                w.Write([]byte("Bad Request"))
                return
            }

            next.ServeHTTP(w, r)

            fmt.Println("after api call")
	})
}

Explanation

  1. Client side make http request
  2. http.Handler will go through the middleware function before heading to the route function
  3. We can check and verify many things before we move on to the route function such as
    • Check the http request method
    • Verify the origin and referer. The referer will follow whatever from the front-end url (including routes and query parameters) if the Referrer-Policy is strict-origin-when-cross-origin
    • Security check such as getting the JWT token from Authorization header r.Header.Get("Authorization") and verify the token.
  4. After it passes all conditions, we can now serve the actual API route function by calling next.ServeHTTP(w, r)
  5. In case we might want to execute another actions after our API is successfully response to the client side.
  6. It’s quite easy in Go as we just need to add our code right after the next.ServeHTTP(w, r) function.
comments powered by Disqus