Dependency Injection With Golang HTTP Middleware
by Justin Michalicek on Feb. 11, 2018, 4:14 p.m. UTC
I have been playing with Go on and off for a couple of months now and am really enjoying it. I could probably do a post just on why and what I like, but in short, it adheres to and encourages simplicity, obviousness, and explicitness that Python does (moreso than Python in many cases) while adding some new tools, implementation options, and deployment options.
Background
Part of the explicitness and obviousness of Go is in function signatures. In Python you can have named parameters with defaults, so they are optional. You even have the *args and **kwargs parameters which take variable, unspecified positional and named parameters. This pretty strongly violates Python's "Explicit is better than implicit" rule since the named parameter with defaults is implicit and *args and **kwargs are not even clearly options without digging into the source code of the function or method. Go allows no such thing. There are no named parameters, there are no optional parameters, and there certainly are no unmentioned wildcard, anything goes parameter collections (there are some options for getting around this if you really, really insist on putting in more effort just to shoot yourself in the foot). All arguments to a function must be specified, are typed, are positional, and must have some value passed in even if it is nil in the case of a pointer. This is great. It's explicit and it's obvious what the function expects from the caller.
Go also encourages using dependency injection, so you will create things such as database connections, callback functions, etc. Anything which may be configurable due to differences in environment such as dev vs staging vs production or for distributable code used by others or passing in mock objects in test cases. This builds upon the explicitness, making it clear what is needed and what will be done, while maintaining flexibility and configurability as opposed to using global state for settings and databases for ease of access anywhere in the code at the cost of explicitness as you will commonly see in web frameworks.
A Small Snag
There is one place that I have found so far where that level of strictness gave me some trouble as a newcomer to the language who is used to being able to just kind of do whatever I need to as in Python. That would be HTTP middleware. In Go HTTP middleware should satisfy the http.Handler interface, frequently achieved with http.HandlerFunc and it should be chainable with other middleware. The standard for making it chainable is that it accepts and http.Handler and returns an http.Handler. That gives us a function looking something like this.
func fooMiddleWare(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { next.ServeHTTP(rw, r) } }
But what if the fooMiddleware needs a database connection? The idiomatic, Go approved way to handle this is to pass the database connection in as an argument to the function. Now you've got a middleware with a function signature like this.
func fooMiddleWare(next http.Handler, db *sqlx.DB) http.Handler
If that is done, the function no longer adheres to the expected pattern for middleware and is not chainable with other middleware, unless you write that middleware specifically to work with this. You could use global state to access the database, but that reduces clarity and explicitness - now you need to configure the database and have it globally accessible, but it's not clear from the function signature that it needs you to do so.
The Solution
A great solution in this case is a factory or constructor function. In Go it is common to have a function which takes some arguments and returns whatever object you need. In this case, a function which takes your dependencies, creates your middleware function wrapping them in a closure, and returns the middleware you need. You may then use that factory to return a function with your dependencies, but still adhering to the expected chainable middleware function signature.
func NewFooMiddleWare(db *sqlx.DB) func(http.Handler) http.Handler { func fooMiddleWare(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // Using the db passed into the constructor, closing over it here. query = db.Rebind("SELECT f.name FROM foos f WHERE f.id=?") f := Foo{} err := db.Get(&f, query, 1) next.ServeHTTP(rw, r) } } }
You can then use that constructor elsewhere in your code like this
package main import ( "fooMiddlewarePackage" "github.com/jmoiron/sqlx" "log" "net/http" "os" _ "github.com/lib/pq" ) // A couple of dummy middlewares for chaining with the constructed middleware func outterMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("outterMiddleware") next.ServeHTTP(w, r) }) } func innerMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("innerMiddleware") next.ServeHTTP(w, r) }) } connectionString := "host=localhost port=5432 user=me password=apassword dbname=fooDB sslmode=disable" db, _ := sqlx.Connect("postgres", connectionString) fooHandler := fooMiddlewarePackage.NewFooMiddleWare(db) http.Handle("/", outerMiddleware(fooHandler(innerMiddleware()))) http.ListenAndServe(":3000", nil)