Good cake is the one you can easily slice into parts with no crumbs falling apart. That’s all this project is about: 3 simple parts, no nasty additives. In part 1 and part 2 I’ve explained the basics of setting up golang project using docker, creating configurable server, interacting with DB, adding routes and handlers.
What happens when multiple handlers have to reuse same logic?
What is the most elegant way of solving this issue?
This is where the middleware comes into play and will be my main focus here.
The idea of a middleware in context of route handlers is that we construct our handlers from multiple, small functions — middlewares. That allows easy code re-usability and handlers become much easier to reason about.
I’ll continue working on users resource from previous posts by adding additional logic to it. One common thing each handler might have is request logging. Before implementing it, there’s a small utility function that will help me construct handlers:
Chain function will accept any number of functions of a Middleware type and call them one by one giving us full control of the flow, more on that later. Now that I have this in place, I’ll add simple request logging middleware. Since it’s going to be reused in multiple handlers, I’ll put it in pkg/middleware directory. All handler specific middleware will live next to handler itself:
Simple higher order function that accepts and returns function of a
httprouter.Handle type and reads some data from request in between. Next I plan to accept user id as url parameter and I want to make sure it’s a valid numeric string. If it’s not, I’ll simply respond to client with status code 412. Sounds like this logic can be implemented as a middleware:
Same principle as in logging middleware. Context is a standard way of sharing data between middleware and I added user id to it for demonstration purposes. If client sends malformed id, I’m skipping all following steps in my middleware chain by simply not calling the
next function and responding to client directly from here.
Next step is
I first create a slice of all required middleware which later becomes a variadic function parameter.
middleware.Chain is implemented in a way that it calls functions right to left so first LogRequest is going to be called followed by validateRequest and getUser.
Few things to point out here. Notice that middleware in mdw slice has slightly different signature than getUser. This is because getUser is our last step in the chain and it will not be calling
nextfunction to jump to next steps of a chain. Second thing is how I read value from context. Let’s start the service before testing the handler:
docker-compose up --build
Now let’s make a request with foobar as user id. Since foobar is not numeric string, I’d expect malformed id message in response:
Now, let’s try valid id. Currently I have no record with id
2in users table so would expect user does not exist message:
You can find whole project here. I really hope this will be a good starting point for you and help building new, exciting things.
Stay safe, eat cake and keep coding!