Skip to content

Instantly share code, notes, and snippets.

@joyrexus
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joyrexus/1e1dcb63b64653229a9e to your computer and use it in GitHub Desktop.
Save joyrexus/1e1dcb63b64653229a9e to your computer and use it in GitHub Desktop.
rest api demo w/ go

Quick demo showing how to to simulate a simple REST API.

Credit where due: this demo is based on this example from Ardan Studios' training materials. We're using httprouter for routing and added a retrieve handler for retrieving users by ID (/users/:id).

See also this example, reflecting basic practices for designing a REST API.

Usage

Start the server:

go run server.go

Create new users:

curl -d "name=jack"          \
     -d "email=jack@foo.org" \
     -d "phone=444-444-4444" localhost:4000/users

curl -d "name=jill"          \
     -d "email=jill@foo.org" \
     -d "phone=555-555-5555" localhost:4000/users

... or just run the attached post.sh script to avoid all that typing:

$ . post.sh 
creating jack ...
creating jill ...
HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 31 Mar 2015 18:23:16 GMT
Content-Length: 214

[{"Id":"0006bc90-d7d3-11e4-8cd5-1093e9029432","Name":"jack","Email":"jack@foo.org","Phone":"444-444-4444"},{"Id":"0009f23f-d7d3-11e4-8cd5-1093e9029432","Name":"jill","Email":"jill@foo.org","Phone":"555-555-5555"}]

List users:

curl -i localhost:4000/users | jq .

The -i flag indicates we want to see the response header as well, so we should get something like ...

HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 31 Mar 2015 17:07:04 GMT
Content-Length: 116
[
  {
    "Phone": "444-444-4444",
    "Email": "jack@foo.org",
    "Name": "jack",
    "Id": "19ea9175-d7d0-11e4-92b1-1093e9029432"
  },
  {
    "Phone": "555-555-5555",
    "Email": "jill@foo.org",
    "Name": "jill",
    "Id": "2e593381-d7d0-11e4-92b1-1093e9029432"
  }
]

Retrieve a user by ID:

curl localhost:4000/users/19ea9175-d7d0-11e4-92b1-1093e9029432
  {
    "Phone": "444-444-4444",
    "Email": "jack@foo.org",
    "Name": "jack",
    "Id": "19ea9175-d7d0-11e4-92b1-1093e9029432"
  }

Search for a user by name:

curl localhost:4000/search?q=jill
  {
    "Phone": "555-555-5555",
    "Email": "jill@foo.org",
    "Name": "jill",
    "Id": "2e593381-d7d0-11e4-92b1-1093e9029432"
  }
# make users
URL=localhost:4000/users
echo "creating jack ..."
curl -d "name=jack" \
-d "email=jack@foo.org" \
-d "phone=444-444-4444" $URL
echo "creating jill ..."
curl -d "name=jill" \
-d "email=jill@foo.org" \
-d "phone=555-555-5555" $URL
curl -i $URL
package main
import (
"encoding/json"
"net/http"
"strings"
"github.com/satori/go.uuid"
"github.com/julienschmidt/httprouter"
)
// user represents a user in the system.
type user struct {
Id string
Name string
Email string
Phone string
}
// users is a slice of users.
var users []user
func main() {
mux := httprouter.New()
mux.GET("/users", list) // list users
mux.POST("/users", create) // create a user
mux.DELETE("/users", refuse) // not allowed
mux.GET("/users/:id", retrieve) // retrieve user with id
mux.GET("/search", search) // search users by name
http.ListenAndServe(":4000", mux)
}
// list returns a JSON representation of existing users.
func list(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
send(w, http.StatusOK, users)
}
// create creates a new user.
func create(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
u := user{
Id: uuid.NewV1().String(),
Name: r.PostFormValue("name"),
Email: r.PostFormValue("email"),
Phone: r.PostFormValue("phone"),
}
users = append(users, u)
http.Redirect(w, r, "/users", http.StatusSeeOther)
}
// retrieve returns the user with the specified ID (`/users/:id`).
func retrieve(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
id := p.ByName("id")
for _, u := range users {
if u.Id == id {
send(w, http.StatusOK, u)
return
}
}
http.Error(w, "Not Found", http.StatusNotFound)
}
// search returns the first matching user (`/search/?q=jill`).
func search(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
query := r.URL.Query().Get("q")
if query == "" {
http.Error(w, "Query Required", http.StatusBadRequest)
return
}
for _, u := range users {
if strings.Contains(u.Name, query) {
send(w, http.StatusOK, u)
return
}
}
http.Error(w, "Not Found", http.StatusNotFound)
}
// send returns a JSON encoded representation of `val`
// with status code `code`.
func send(w http.ResponseWriter, code int, val interface{}) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
return json.NewEncoder(w).Encode(val)
}
// refuse returns a `Method Not Allowed` response.
func refuse(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
http.Error(w, "Not Allowed", http.StatusMethodNotAllowed)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment