Skip to content

Instantly share code, notes, and snippets.

@alexedwards alexedwards/_tree
Last active Jun 14, 2019

Embed
What would you like to do?
.
├── books
│   ├── handlers.go
│   └── models.go
├── config
│   └── db.go
└── main.go
package config
import (
"database/sql"
_ "github.com/lib/pq"
)
type Env struct {
DB *sql.DB
}
func NewDB(dataSourceName string) (*sql.DB, error) {
db, err := sql.Open("postgres", dataSourceName)
if err != nil {
return nil, err
}
if err = db.Ping(); err != nil {
return nil, err
}
return db, nil
}
package books
import (
"bookstore/config"
"fmt"
"net/http"
)
func BooksIndex(env *config.Env) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, http.StatusText(405), 405)
return
}
bks, err := AllBooks(env.DB)
if err != nil {
http.Error(w, http.StatusText(500), 500)
return
}
for _, bk := range bks {
fmt.Fprintf(w, "%s, %s, %s, £%.2f\n", bk.Isbn, bk.Title, bk.Author, bk.Price)
}
})
}
package main
import (
"bookstore/books"
"bookstore/config"
"log"
"net/http"
)
func main() {
db, err := config.NewDB("postgres://user:pass@localhost/bookstore")
if err != nil {
log.Panic(err)
}
defer db.Close()
env := &config.Env{DB: db}
http.Handle("/books", books.BooksIndex(env))
http.ListenAndServe(":3000", nil)
}
package books
import (
"database/sql"
)
type Book struct {
Isbn string
Title string
Author string
Price float32
}
func AllBooks(db *sql.DB) ([]*Book, error) {
rows, err := db.Query("SELECT * FROM books")
if err != nil {
return nil, err
}
defer rows.Close()
bks := make([]*Book, 0)
for rows.Next() {
bk := new(Book)
err := rows.Scan(&bk.Isbn, &bk.Title, &bk.Author, &bk.Price)
if err != nil {
return nil, err
}
bks = append(bks, bk)
}
if err = rows.Err(); err != nil {
return nil, err
}
return bks, nil
}
@harlow

This comment has been minimized.

Copy link

commented Aug 2, 2016

Great example @alexedwards. If you're logging from Middleware how would you handle the call chain so Env is avail in the middleware too?

@nkumar15

This comment has been minimized.

Copy link

commented Jun 17, 2017

Any suggestion on @harlow question? I am also thinking about this question. What if I want to share logger object in middleware and data access functions?

@hypnoglow

This comment has been minimized.

Copy link

commented Jul 6, 2017

@nkumar15 What is the problem?
This will work since BooksIndex(env) returns http.Handler.
And you can pass logger directly to the middleware.

http.Handle("/books", loggerMiddleware(books.BooksIndex(env), env.Logger))
http.ListenAndServe(":3000", nil)
@paracha3

This comment has been minimized.

Copy link

commented Feb 9, 2018

Not clear how (specially where) the db.Close() will be done? I am surprised i don't see defer db.Close() anywhere.
What am i missing?

@aarjan

This comment has been minimized.

Copy link

commented Mar 5, 2018

@paracha3, you can defer the db close operation in the main function, just after error check; since it is the root node where db is instantiated.

@mlevkov

This comment has been minimized.

Copy link

commented May 27, 2018

a concrete example with logging and middleware would have been nice. I would not want to instantiate the data elements in every package, but rather pass them around once initiated.

@TFiroozian

This comment has been minimized.

Copy link

commented Nov 7, 2018

Have you seen the interface way? what if I want to have multiple packages in interface way .Should I make a config package as same as here?

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.