package main import ( "database/sql" "encoding/json" "net/http" ) type api struct { db *sql.DB } type post struct { ID int Title string Body string } func (a *api) posts(w http.ResponseWriter, r *http.Request) { rows, err := a.db.Query("SELECT id, title, body FROM posts") if err != nil { a.fail(w, "failed to fetch posts: "+err.Error(), 500) return } defer rows.Close() var posts []*post for rows.Next() { p := &post{} if err := rows.Scan(&p.ID, &p.Title, &p.Body); err != nil { a.fail(w, "failed to scan post: "+err.Error(), 500) return } posts = append(posts, p) } if rows.Err() != nil { a.fail(w, "failed to read all posts: "+rows.Err().Error(), 500) return } data := struct { Posts []*post }{posts} a.ok(w, data) } func main() { // @NOTE: the real connection is not required for tests db, err := sql.Open("mysql", "root@/blog") if err != nil { panic(err) } app := &api{db: db} http.HandleFunc("/posts", app.posts) http.ListenAndServe(":8080", nil) } func (a *api) fail(w http.ResponseWriter, msg string, status int) { w.Header().Set("Content-Type", "application/json") data := struct { Error string }{Error: msg} resp, _ := json.Marshal(data) w.WriteHeader(status) w.Write(resp) } func (a *api) ok(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "application/json") resp, err := json.Marshal(data) if err != nil { w.WriteHeader(http.StatusInternalServerError) a.fail(w, "oops something evil has happened", 500) return } w.Write(resp) }