MCPcopy Index your code
hub / github.com/ant0ine/go-json-rest

github.com/ant0ine/go-json-rest @v3.3.2 sqlite

repository ↗ · DeepWiki ↗ · release v3.3.2 ↗
242 symbols 1,001 edges 46 files 139 documented · 57%
README

Go-Json-Rest

A quick and easy way to setup a RESTful JSON API

godoc license build

Go-Json-Rest is a thin layer on top of net/http that helps building RESTful JSON APIs easily. It provides fast and scalable request routing using a Trie based implementation, helpers to deal with JSON requests and responses, and middlewares for functionalities like CORS, Auth, Gzip, Status ...

Table of content

Features

  • Many examples.
  • Fast and scalable URL routing. It implements the classic route description syntax using a Trie data structure.
  • Architecture based on a router(App) sitting on top of a stack of Middlewares.
  • The Middlewares implement functionalities like Logging, Gzip, CORS, Auth, Status, ...
  • Implemented as a net/http Handler. This standard interface allows combinations with other Handlers.
  • Test package to help writing tests for your API.
  • Monitoring statistics inspired by Memcached.

Install

This package is "go-gettable", just do:

go get github.com/ant0ine/go-json-rest/rest

Vendoring

The recommended way of using this library in your project is to use the "vendoring" method, where this library code is copied in your repository at a specific revision. This page is a good summary of package management in Go.

Middlewares

Core Middlewares:

Name Description
AccessLogApache Access log inspired by Apache mod_log_config
AccessLogJson Access log with records as JSON
AuthBasic Basic HTTP auth
ContentTypeChecker Verify the request content type
Cors CORS server side implementation
Gzip Compress the responses
If Conditionally execute a Middleware at runtime
JsonIndent Easy to read JSON
Jsonp Response as JSONP
PoweredBy Manage the X-Powered-By response header
Recorder Record the status code and content length in the Env
Status Memecached inspired stats about the requests
Timer Keep track of the elapsed time in the Env

Third Party Middlewares:

Name Description
Statsd Send stats to a statsd server
JWT Provides authentication via Json Web Tokens
AuthToken Provides a Token Auth implementation
ForceSSL Forces SSL on requests
SecureRedirect Redirect clients from HTTP to HTTPS

If you have a Go-Json-Rest compatible middleware, feel free to submit a PR to add it in this list, and in the examples.

Examples

All the following examples can be found in dedicated examples repository: https://github.com/ant0ine/go-json-rest-examples

Basics

First examples to try, as an introduction to go-json-rest.

Hello World!

Tradition!

curl demo:

curl -i http://127.0.0.1:8080/

code:

package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net/http"
)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    api.SetApp(rest.AppSimple(func(w rest.ResponseWriter, r *rest.Request) {
        w.WriteJson(map[string]string{"Body": "Hello World!"})
    }))
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

Lookup

Demonstrate how to use the relaxed placeholder (notation #paramName). This placeholder matches everything until the first /, including .

curl demo:

curl -i http://127.0.0.1:8080/lookup/google.com
curl -i http://127.0.0.1:8080/lookup/notadomain

code:

package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net"
    "net/http"
)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    router, err := rest.MakeRouter(
        rest.Get("/lookup/#host", func(w rest.ResponseWriter, req *rest.Request) {
            ip, err := net.LookupIP(req.PathParam("host"))
            if err != nil {
                rest.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            w.WriteJson(&ip)
        }),
    )
    if err != nil {
        log.Fatal(err)
    }
    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

Countries

Demonstrate simple POST GET and DELETE operations

curl demo:

curl -i -H 'Content-Type: application/json' \
    -d '{"Code":"FR","Name":"France"}' http://127.0.0.1:8080/countries
curl -i -H 'Content-Type: application/json' \
    -d '{"Code":"US","Name":"United States"}' http://127.0.0.1:8080/countries
curl -i http://127.0.0.1:8080/countries/FR
curl -i http://127.0.0.1:8080/countries/US
curl -i http://127.0.0.1:8080/countries
curl -i -X DELETE http://127.0.0.1:8080/countries/FR
curl -i http://127.0.0.1:8080/countries
curl -i -X DELETE http://127.0.0.1:8080/countries/US
curl -i http://127.0.0.1:8080/countries

code:

package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net/http"
    "sync"
)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    router, err := rest.MakeRouter(
        rest.Get("/countries", GetAllCountries),
        rest.Post("/countries", PostCountry),
        rest.Get("/countries/:code", GetCountry),
        rest.Delete("/countries/:code", DeleteCountry),
    )
    if err != nil {
        log.Fatal(err)
    }
    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

type Country struct {
    Code string
    Name string
}

var store = map[string]*Country{}

var lock = sync.RWMutex{}

func GetCountry(w rest.ResponseWriter, r *rest.Request) {
    code := r.PathParam("code")

    lock.RLock()
    var country *Country
    if store[code] != nil {
        country = &Country{}
        *country = *store[code]
    }
    lock.RUnlock()

    if country == nil {
        rest.NotFound(w, r)
        return
    }
    w.WriteJson(country)
}

func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
    lock.RLock()
    countries := make([]Country, len(store))
    i := 0
    for _, country := range store {
        countries[i] = *country
        i++
    }
    lock.RUnlock()
    w.WriteJson(&countries)
}

func PostCountry(w rest.ResponseWriter, r *rest.Request) {
    country := Country{}
    err := r.DecodeJsonPayload(&country)
    if err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    if country.Code == "" {
        rest.Error(w, "country code required", 400)
        return
    }
    if country.Name == "" {
        rest.Error(w, "country name required", 400)
        return
    }
    lock.Lock()
    store[country.Code] = &country
    lock.Unlock()
    w.WriteJson(&country)
}

func DeleteCountry(w rest.ResponseWriter, r *rest.Request) {
    code := r.PathParam("code")
    lock.Lock()
    delete(store, code)
    lock.Unlock()
    w.WriteHeader(http.StatusOK)
}

Users

Demonstrate how to use Method Values.

Method Values have been introduced in Go 1.1.

This shows how to map a Route to a method of an instantiated object (i.e: receiver of the method)

curl demo:

curl -i -H 'Content-Type: application/json' \
    -d '{"Name":"Antoine"}' http://127.0.0.1:8080/users
curl -i http://127.0.0.1:8080/users/0
curl -i -X PUT -H 'Content-Type: application/json' \
    -d '{"Name":"Antoine Imbert"}' http://127.0.0.1:8080/users/0
curl -i -X DELETE http://127.0.0.1:8080/users/0
curl -i http://127.0.0.1:8080/users

code:

package main

import (
    "fmt"
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net/http"
    "sync"
)

func main() {

    users := Users{
        Store: map[string]*User{},
    }

    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    router, err := rest.MakeRouter(
        rest.Get("/users", users.GetAllUsers),
        rest.Post("/users", users.PostUser),
        rest.Get("/users/:id", users.GetUser),
        rest.Put("/users/:id", users.PutUser),
        rest.Delete("/users/:id", users.DeleteUser),
    )
    if err != nil {
        log.Fatal(err)
    }
    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

type User struct {
    Id   string
    Name string
}

type Users struct {
    sync.RWMutex
    Store map[string]*User
}

func (u *Users) GetAllUsers(w rest.ResponseWriter, r *rest.Request) {
    u.RLock()
    users := make([]User, len(u.Store))
    i := 0
    for _, user := range u.Store {
        users[i] = *user
        i++
    }
    u.RUnlock()
    w.WriteJson(&users)
}

func (u *Users) GetUser(w rest.ResponseWriter, r *rest.Request) {
    id := r.PathParam("id")
    u.RLock()
    var user *User
    if u.Store[id] != nil {
        user = &User{}
        *user = *u.Store[id]
    }
    u.RUnlock()
    if user == nil {
        rest.NotFound(w, r)
        return
    }
    w.WriteJson(user)
}

func (u *Users) PostUser(w rest.ResponseWriter, r *rest.Request) {
    user := User{}
    err := r.DecodeJsonPayload(&user)
    if err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    u.Lock()
    id := fmt.Sprintf("%d", len(u.Store)) // stupid
    user.Id = id
    u.Store[id] = &user
    u.Unlock()
    w.WriteJson(&user)
}

func (u *Users) PutUser(w rest.ResponseWriter, r *rest.Request) {
    id := r.PathParam("id")
    u.Lock()
    if u.Store[id] == nil {
        rest.NotFound(w, r)
        u.Unlock()
        return
    }
    user := User{}
    err := r.DecodeJsonPayload(&user)
    if err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        u.Unlock()
        return
    }
    user.Id = id
    u.Store[id] = &user
    u.Unlock()
    w.WriteJson(&user)
}

func (u *Users) DeleteUser(w rest.ResponseWriter, r *rest.Request) {
    id := r.PathParam("id")
    u.Lock()
    delete(u.Store, id)
    u.Unlock()
    w.WriteHeader(http.StatusOK)
}

Applications

Common use cases, found in many applications.

API and static files

Combine Go-Json-Rest with other handlers.

api.MakeHandler() is a valid http.Handler, and can be combined with other handlers. In this example the api handler is used under the /api/ prefix, while a FileServer is instantiated under the /static/ prefix.

curl demo:

curl -i http://127.0.0.1:8080/api/message
curl -i http://127.0.0.1:8080/static/main.go

code:

package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net/http"
)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)

    router, err := rest.MakeRouter(
        rest.Get("/message", func(w rest.ResponseWriter, req *rest.Request) {
            w.WriteJson(map[string]string{"Body": "Hello World!"})
        }),
    )
    if err != nil {
        log.Fatal(err)
    }
    api.SetApp(router)

    http.Handle("/api/", http.StripPrefix("/api", api.MakeHandler()))

    http.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir("."))))

    log.Fatal(http.ListenAndServe(":8080", nil))
}

GORM

Demonstrate basic CRUD operation using a store based on MySQL and GORM

GORM is simple ORM library for Go. In this example the same struct is used both as the GORM model and as the JSON model.

curl demo:

curl -i -H 'Content-Type: application/json' \
    -d '{"Message":"this is a test"}' http://127.0.0.1:8080/reminders
curl -i http://127.0.0.1:8080/reminders/1
curl -i http://127.0.0.1:8080/reminders
curl -i -X PUT -H 'Content-Type: application/json' \
    -d '{"Message":"is updated"}' http://127.0.0.1:8080/reminders/1
curl -i -X DELETE http://127.0.0.1:8080/reminders/1

code: ``` go package main

import ( "github.com/ant0ine/go-json-rest/rest" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/gorm" "log" "net/http" "time" )

func main() {

i := Impl{}
i.InitDB()
i.InitSchema()

api := rest.NewApi()
api.Use(rest.DefaultDevStack...)
router, err := rest.MakeRouter(
    rest.Get("/reminders", i.GetAllReminders),
    rest.Post("/reminders", i.PostReminder),
    rest.Get("/reminders/:id", i.GetReminder),
    rest.Put("/reminders/:id", i.PutReminder),
    rest.Delete("/reminders/:id", i.DeleteReminder),
)
if err != nil {
    log.Fatal(err)
}
api.SetApp(router)
log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))

}

type Reminder struct { Id int64 json:"id" Message string sql:"size:1024" json:"message" CreatedAt time.Time json:"createdAt" UpdatedAt time.Time json:"updatedAt" DeletedAt time.Time json:"-" }

type Impl struct { DB *gorm.DB }

func (i *Impl) InitDB() { var err error i.DB, err = gorm.Open("mysql", "gorm:gorm@/gorm?charset=utf8&parseTime=True") if err != nil { log.Fatalf("Got error when connect database, the error is '%v'", err) } i.DB.LogMode(true) }

func (i *Impl) InitSchema() { i.DB.AutoMigrate(&Reminder{

Extension points exported contracts — how you extend this code

Middleware (Interface)
Middleware defines the interface that objects must implement in order to wrap a HandlerFunc and be used in the middlewar [15 …
rest/middleware.go
ResponseWriter (Interface)
A ResponseWriter interface dedicated to JSON HTTP response. Note, the responseWriter object instantiated by the framewor
rest/response.go
App (Interface)
App defines the interface that an object should implement to be used as an app in this framework stack. The App is the t [1 …
rest/middleware.go
HandlerFunc (FuncType)
HandlerFunc defines the handler function. It is the go-json-rest equivalent of http.HandlerFunc.
rest/middleware.go
MiddlewareSimple (FuncType)
MiddlewareSimple is an adapter type that makes it easy to write a Middleware with a simple function. eg: api.Use(rest.Mi
rest/middleware.go

Core symbols most depended-on inside this repo

CodeIs
called by 40
rest/test/util.go
AddRoute
called by 33
rest/trie/impl.go
Use
called by 32
rest/api.go
ContentTypeIsJson
called by 31
rest/test/util.go
NewApi
called by 28
rest/api.go
MakeHandler
called by 28
rest/api.go
SetApp
called by 26
rest/api.go
WriteJson
called by 23
rest/response.go

Shape

Method 103
Function 98
Struct 34
Interface 3
FuncType 2
TypeAlias 2

Languages

Go100%

Modules by API surface

rest/trie/impl.go23 symbols
rest/test/util.go15 symbols
rest/response.go15 symbols
rest/access_log_apache.go14 symbols
rest/router_test.go13 symbols
rest/trie/impl_test.go11 symbols
rest/middleware.go11 symbols
rest/request_test.go10 symbols
rest/json_indent.go10 symbols
rest/router.go9 symbols
rest/route.go9 symbols
rest/recorder.go9 symbols

For agents

$ claude mcp add go-json-rest \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact