Skip to content

Instantly share code, notes, and snippets.

@nmerouze
Last active March 2, 2022 16:36
Show Gist options
  • Star 56 You must be signed in to star a gist
  • Fork 19 You must be signed in to fork a gist
  • Save nmerouze/2e26a02d23c4c62173fd to your computer and use it in GitHub Desktop.
Save nmerouze/2e26a02d23c4c62173fd to your computer and use it in GitHub Desktop.
JSON-API with Go and MongoDB: Final Part
package main
import (
"encoding/json"
"log"
"net/http"
"reflect"
"time"
"github.com/gorilla/context"
"github.com/julienschmidt/httprouter"
"github.com/justinas/alice"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// Repo
type Tea struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name"`
Category string `json:"category"`
}
type TeasCollection struct {
Data []Tea `json:"data"`
}
type TeaResource struct {
Data Tea `json:"data"`
}
type TeaRepo struct {
coll *mgo.Collection
}
func (r *TeaRepo) All() (TeasCollection, error) {
result := TeasCollection{[]Tea{}}
err := r.coll.Find(nil).All(&result.Data)
if err != nil {
return result, err
}
return result, nil
}
func (r *TeaRepo) Find(id string) (TeaResource, error) {
result := TeaResource{}
err := r.coll.FindId(bson.ObjectIdHex(id)).One(&result.Data)
if err != nil {
return result, err
}
return result, nil
}
func (r *TeaRepo) Create(tea *Tea) error {
id := bson.NewObjectId()
_, err := r.coll.UpsertId(id, tea)
if err != nil {
return err
}
tea.Id = id
return nil
}
func (r *TeaRepo) Update(tea *Tea) error {
err := r.coll.UpdateId(tea.Id, tea)
if err != nil {
return err
}
return nil
}
func (r *TeaRepo) Delete(id string) error {
err := r.coll.RemoveId(bson.ObjectIdHex(id))
if err != nil {
return err
}
return nil
}
// Errors
type Errors struct {
Errors []*Error `json:"errors"`
}
type Error struct {
Id string `json:"id"`
Status int `json:"status"`
Title string `json:"title"`
Detail string `json:"detail"`
}
func WriteError(w http.ResponseWriter, err *Error) {
w.Header().Set("Content-Type", "application/vnd.api+json")
w.WriteHeader(err.Status)
json.NewEncoder(w).Encode(Errors{[]*Error{err}})
}
var (
ErrBadRequest = &Error{"bad_request", 400, "Bad request", "Request body is not well-formed. It must be JSON."}
ErrNotAcceptable = &Error{"not_acceptable", 406, "Not Acceptable", "Accept header must be set to 'application/vnd.api+json'."}
ErrUnsupportedMediaType = &Error{"unsupported_media_type", 415, "Unsupported Media Type", "Content-Type header must be set to: 'application/vnd.api+json'."}
ErrInternalServer = &Error{"internal_server_error", 500, "Internal Server Error", "Something went wrong."}
)
// Middlewares
func recoverHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic: %+v", err)
WriteError(w, ErrInternalServer)
}
}()
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func loggingHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
t1 := time.Now()
next.ServeHTTP(w, r)
t2 := time.Now()
log.Printf("[%s] %q %v\n", r.Method, r.URL.String(), t2.Sub(t1))
}
return http.HandlerFunc(fn)
}
func acceptHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != "application/vnd.api+json" {
WriteError(w, ErrNotAcceptable)
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func contentTypeHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Content-Type") != "application/vnd.api+json" {
WriteError(w, ErrUnsupportedMediaType)
return
}
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func bodyHandler(v interface{}) func(http.Handler) http.Handler {
t := reflect.TypeOf(v)
m := func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
val := reflect.New(t).Interface()
err := json.NewDecoder(r.Body).Decode(val)
if err != nil {
WriteError(w, ErrBadRequest)
return
}
if next != nil {
context.Set(r, "body", val)
next.ServeHTTP(w, r)
}
}
return http.HandlerFunc(fn)
}
return m
}
// Main handlers
type appContext struct {
db *mgo.Database
}
func (c *appContext) teasHandler(w http.ResponseWriter, r *http.Request) {
repo := TeaRepo{c.db.C("teas")}
teas, err := repo.All()
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/vnd.api+json")
json.NewEncoder(w).Encode(teas)
}
func (c *appContext) teaHandler(w http.ResponseWriter, r *http.Request) {
params := context.Get(r, "params").(httprouter.Params)
repo := TeaRepo{c.db.C("teas")}
tea, err := repo.Find(params.ByName("id"))
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/vnd.api+json")
json.NewEncoder(w).Encode(tea)
}
func (c *appContext) createTeaHandler(w http.ResponseWriter, r *http.Request) {
body := context.Get(r, "body").(*TeaResource)
repo := TeaRepo{c.db.C("teas")}
err := repo.Create(&body.Data)
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/vnd.api+json")
w.WriteHeader(201)
json.NewEncoder(w).Encode(body)
}
func (c *appContext) updateTeaHandler(w http.ResponseWriter, r *http.Request) {
params := context.Get(r, "params").(httprouter.Params)
body := context.Get(r, "body").(*TeaResource)
body.Data.Id = bson.ObjectIdHex(params.ByName("id"))
repo := TeaRepo{c.db.C("teas")}
err := repo.Update(&body.Data)
if err != nil {
panic(err)
}
w.WriteHeader(204)
w.Write([]byte("\n"))
}
func (c *appContext) deleteTeaHandler(w http.ResponseWriter, r *http.Request) {
params := context.Get(r, "params").(httprouter.Params)
repo := TeaRepo{c.db.C("teas")}
err := repo.Delete(params.ByName("id"))
if err != nil {
panic(err)
}
w.WriteHeader(204)
w.Write([]byte("\n"))
}
// Router
type router struct {
*httprouter.Router
}
func (r *router) Get(path string, handler http.Handler) {
r.GET(path, wrapHandler(handler))
}
func (r *router) Post(path string, handler http.Handler) {
r.POST(path, wrapHandler(handler))
}
func (r *router) Put(path string, handler http.Handler) {
r.PUT(path, wrapHandler(handler))
}
func (r *router) Delete(path string, handler http.Handler) {
r.DELETE(path, wrapHandler(handler))
}
func NewRouter() *router {
return &router{httprouter.New()}
}
func wrapHandler(h http.Handler) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
context.Set(r, "params", ps)
h.ServeHTTP(w, r)
}
}
func main() {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
appC := appContext{session.DB("test")}
commonHandlers := alice.New(context.ClearHandler, loggingHandler, recoverHandler, acceptHandler)
router := NewRouter()
router.Get("/teas/:id", commonHandlers.ThenFunc(appC.teaHandler))
router.Put("/teas/:id", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.updateTeaHandler))
router.Delete("/teas/:id", commonHandlers.ThenFunc(appC.deleteTeaHandler))
router.Get("/teas", commonHandlers.ThenFunc(appC.teasHandler))
router.Post("/teas", commonHandlers.Append(contentTypeHandler, bodyHandler(TeaResource{})).ThenFunc(appC.createTeaHandler))
http.ListenAndServe(":8080", router)
}
@st3fan
Copy link

st3fan commented Jul 11, 2016

Aren't you supposed to Copy the MongoDB session per request?

@ijunaid8989
Copy link

@nmerouze out of interest have you moved on to Elixir?

@robincher
Copy link

Thanks for sharing!

@yusfianG
Copy link

please help, i have probeleum how to save consume api result to mongo
my code its error !!!

package main

import (
"encoding/json"
"fmt"
"bytes"
"io/ioutil"
"net/http"
//"mydb"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)

type Response struct {
StatusCode int json:"statusCode"
Headers map[string]string json:"headers"
Body string json:"body"
}

type Passanger struct {
bson.ObjectId json:"id" bson:"_id"
Title string json:"title" bson:"title"
Image string json:"image" bson:"image"
Price string json:"price" bson:"price"
Rating string json:"rating" bson:"rating"
}

//type Passanger struct {
//ID bson.ObjectId bson:"_id,omitempty"
//Title string
//Image string
//Price string
//Rating string
//}

var (
IsDrop = true
)

type Adapter func(http.Handler) http.Handler
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range adapters {
h = adapter(h)
}
return h
}

func main() {
fmt.Println("Starting the application...")

url := "http://" 
fmt.Println("URL:>", url)


jsonData := map[string]string{}
jsonValue, _ := json.Marshal(jsonData)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue))
req.Header.Set("Content-Type", "application/json")

// req.Header.Set("Authorization", strAuthorization)

client := &http.Client{}

response, err := client.Do(req)

var info map[string]interface{}
var infodata string

if err != nil {
    fmt.Printf("The HTTP request failed with error %s\n", err)
} else {
    data, _ := ioutil.ReadAll(response.Body)
	//defer response.Body.Close()
	//json.Unmarshal(data, &info)
    fmt.Printf("ALTEA Response: %s\n", string(data))	    		
}


fmt.Println("Save Data To Database...")


session, err := mgo.Dial("localhost")
if err != nil {
	panic(err)
}

defer session.Close()

session.SetMode(mgo.Monotonic, true)

// Drop Database
if IsDrop {
	err = session.DB("test").DropDatabase()
	if err != nil {
		panic(err)
	}
}

// Collection Passanger
c := session.DB("flylist").C("passanger")

// Index
index := mgo.Index{
	Key:        []string{"title", "image", "price", "rating"},
	Unique:     true,
	DropDups:   true,
	Background: true,
	Sparse:     true,
}

err = c.EnsureIndex(index)
if err != nil {
	panic(err)
}


// Insert Datas
infodata = info["data"].(map[string]interface{})["id"].(string)
//konversi:= []byte(info)
err = c.Insert(&Passanger{Title: string(infodata[0]), Image: string(infodata[1]), Price: string(infodata[2]), Rating: string(infodata[3])})
if err != nil {
	panic(err)

}

// Query All
var results []Passanger
err = c.Find(bson.M{"name": "Ale"}).Sort("-timestamp").All(&results)

if err != nil {
	panic(err)
}
fmt.Println("Results All: ", results)

}

Please help Me ,!

@yusfianG
Copy link

Please help , i have problem when try to save consume api result to mongodb, the code it's error

package main

import (
"encoding/json"
"fmt"
"bytes"
"io/ioutil"
"net/http"
//"mydb"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)

type Response struct {
StatusCode int json:"statusCode"
Headers map[string]string json:"headers"
Body string json:"body"
}

type Passanger struct {
bson.ObjectId json:"id" bson:"_id"
Title string json:"title" bson:"title"
Image string json:"image" bson:"image"
Price string json:"price" bson:"price"
Rating string json:"rating" bson:"rating"
}

//type Passanger struct {
//ID bson.ObjectId bson:"_id,omitempty"
//Title string
//Image string
//Price string
//Rating string
//}

var (
IsDrop = true
)

type Adapter func(http.Handler) http.Handler
func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
for _, adapter := range adapters {
h = adapter(h)
}
return h
}

func main() {
fmt.Println("Starting the application...")

url := "http://" 
fmt.Println("URL:>", url)


jsonData := map[string]string{}
jsonValue, _ := json.Marshal(jsonData)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonValue))
req.Header.Set("Content-Type", "application/json")

// req.Header.Set("Authorization", strAuthorization)

client := &http.Client{}

response, err := client.Do(req)

var info map[string]interface{}
var infodata string

if err != nil {
    fmt.Printf("The HTTP request failed with error %s\n", err)
} else {
    data, _ := ioutil.ReadAll(response.Body)
	//defer response.Body.Close()
	//json.Unmarshal(data, &info)
    fmt.Printf("ALTEA Response: %s\n", string(data))	    		
}


fmt.Println("Save Data To Database...")


session, err := mgo.Dial("localhost")
if err != nil {
	panic(err)
}

defer session.Close()

session.SetMode(mgo.Monotonic, true)

// Drop Database
if IsDrop {
	err = session.DB("test").DropDatabase()
	if err != nil {
		panic(err)
	}
}

// Collection Passanger
c := session.DB("flylist").C("passanger")

// Index
index := mgo.Index{
	Key:        []string{"title", "image", "price", "rating"},
	Unique:     true,
	DropDups:   true,
	Background: true,
	Sparse:     true,
}

err = c.EnsureIndex(index)
if err != nil {
	panic(err)
}


// Insert Datas
infodata = info["data"].(map[string]interface{})["id"].(string)
//konversi:= []byte(info)
err = c.Insert(&Passanger{Title: string(infodata[0]), Image: string(infodata[1]), Price: string(infodata[2]), Rating: string(infodata[3])})
if err != nil {
	panic(err)

}

// Query All
var results []Passanger
err = c.Find(bson.M{"name": "Ale"}).Sort("-timestamp").All(&results)

if err != nil {
	panic(err)
}
fmt.Println("Results All: ", results)

}

Please correct my code, thanks alot before

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment