Instantly share code, notes, and snippets.

@alexedwards /_tree
Last active Dec 18, 2018

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

harlow 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

nkumar15 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

hypnoglow 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

paracha3 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

aarjan 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

mlevkov 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

TFiroozian 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