Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
HTTP Closures
package main
import (
"net/http"
"database/sql"
"fmt"
"log"
"os"
)
func helloHandler(db *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var name string
// Execute the query.
row := db.QueryRow("SELECT myname FROM mytable")
if err := row.Scan(&name); err != nil {
http.Error(w, err.Error(), 500)
return
}
// Write it back to the client.
fmt.Fprintf(w, "hi %s!\n", name)
})
}
func withMetrics(l *log.Logger, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
began := time.Now()
next.ServeHTTP(w, r)
l.Printf("%s %s took %s", r.Method, r.URL, time.Since(began))
})
}
func main() {
// Open our database connection.
db, err := sql.Open("postgres", "…")
if err != nil {
log.Fatal(err)
}
// Create our logger
logger := log.New(os.Stdout, "", 0)
// Register our handler.
http.Handle("/hello", helloHandler(db))
// Register our handler with metrics logging
http.Handle("/hello_again", withMetrics(logger, helloHandler(db)))
http.ListenAndServe(":8080", nil)
}
@alexedwards

This comment has been minimized.

Copy link

@alexedwards alexedwards commented Oct 4, 2014

I really like this approach.

Just wondering - Is there any advantage/disadvantage of doing it the above way instead of creating a custom DB type and defining handlers as methods against it? Like so...

type MyDB struct {
  *sql.DB
}

func(db *MyDB) handlerOne(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Do something databasey
  })
}

func(db *MyDB) handlerTwo() http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Do something else databasey
  })
}

func main() {
  db, err := sql.Open("postgres", "…")
  // Handle
  mydb := &MyDB{db}
  http.Handle("/", myDB.handlerOne(myDB.handlerTwo()))
  // Etc.
}

It seems to me that the nice thing with this is that the middleware handler (handlerOne) retains the form func (http.Handler) http.Handler meaning it can be used nicely for chaining in the Alice style.

@crossle

This comment has been minimized.

Copy link

@crossle crossle commented Aug 31, 2017

Why not store db connect to context?

@Jeiwan

This comment has been minimized.

Copy link

@Jeiwan Jeiwan commented Dec 25, 2017

@crossle DB connections are usually opened once, when the app starts, and reused later. Contexts are for things that are started/initialized/created per request or per operation.

@gerep

This comment has been minimized.

Copy link

@gerep gerep commented Jan 4, 2018

@crossle Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions. - https://godoc.org/golang.org/x/net/context

@mikeschinkel

This comment has been minimized.

Copy link

@mikeschinkel mikeschinkel commented Apr 14, 2018

This was super helpful to me as I have been learning GoLang. Thanks for posting!

@ldelossa

This comment has been minimized.

Copy link

@ldelossa ldelossa commented Apr 26, 2018

+1 this is good.

@smurariu

This comment has been minimized.

Copy link

@smurariu smurariu commented May 8, 2018

@alexedwards I don't think there's an advantage or disadvantage but it does somewhat limit the explosion of types/interfaces. And I'm guessing that in some scenarios you'll want to use your approach in others this one so it's really useful to be aware of both.

@montera82

This comment has been minimized.

Copy link

@montera82 montera82 commented May 23, 2018

and how do you write a test against the handler hellohandler using this approach?

@adam-erickson

This comment has been minimized.

Copy link

@adam-erickson adam-erickson commented May 23, 2018

The main issue with the @tsenart method is if both your handlers and handles live in a separate package from your main database instance. This still requires creating a struct or global variable in order to pass the database to these functions from main. Hence, I don't consider it a very realistic solution for large projects. In terms of package layout and compartmentalization, the method used here looks very nice: https://hackernoon.com/make-yourself-a-go-web-server-with-mongodb-go-on-go-on-go-on-48f394f24e

@Carlislegroup

This comment has been minimized.

Copy link

@Carlislegroup Carlislegroup commented Jun 20, 2018

Diving into best practices to separate DB from code for scalability/flexibility, and came here from ben johnson's post (https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091). Thank you for putting this together. Is there any blog posts on how to write tests against this approach? Thanks!

@ace3

This comment has been minimized.

Copy link

@ace3 ace3 commented Jul 1, 2018

+1 very helpful

@blinderjay

This comment has been minimized.

Copy link

@blinderjay blinderjay commented May 14, 2019

smarter way to organize golang code

@Gimongi

This comment has been minimized.

Copy link

@Gimongi Gimongi commented Oct 14, 2020

and how do you write a test against the handler hellohandler using this approach?

Execute helloHandler and put the returned HandlerFunc into a var

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.