-
-
Save tsenart/5fc18c659814c078378d to your computer and use it in GitHub Desktop.
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) | |
} |
+1 this is good.
@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.
and how do you write a test against the handler hellohandler
using this approach?
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
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!
+1 very helpful
smarter way to organize golang code
and how do you write a test against the handler
hellohandler
using this approach?
Execute helloHandler and put the returned HandlerFunc into a var
and how do you write a test against the handler
hellohandler
using this approach?Execute helloHandler and put the returned HandlerFunc into a var
I guess writing unit test this way is again unrealistic, just imagine if there are thousands of handlers to be tested.
This was super helpful to me as I have been learning GoLang. Thanks for posting!