Go: Simple, Easy, Fast – Building a Go (golang) REST Service with Gorilla

Post to Twitter

There are a lot of tools out there for Go (golang) to build REST based services. Gorilla is a web toolkit for the Go programming language. With Gorilla you can build RPC services, WebSocket apps and many more. Today I’m going to go over building a simple REST service using Gorilla (Mux).

Note: I assume at this point you’ve already installed Go and setup your Go environment and have it working.

Inside my Go project structure I’m going to create a folder called GorillaRestExample. Inside of that folder I’ll add a single file called main.go.

$ touch main.go

Let’s start off with a simple Hello World style application. Add the following code to the main.go file:

You can run the code easily from the same folder as the source code file:

$ go run main.go 

Test it via cURL (or you can use a web browser):

$ curl -X GET http://127.0.0.1:8080/hello/chad -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /hello/chad HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Mon, 05 Jul 2015 16:15:27 GMT
< Content-Length: 12
< Content-Type: text/plain; charset=utf-8
< 
Hello: chad

In the main() function we use Gorilla (Mux) to setup a single route for /hello/{name} with {name} being something that is entered by the end user. In the index() handler we log a few items of interest and then extract the {name} value out of the query string.

vars := mux.Vars(r)
name := vars["name"]

Finally we respond with an HTTP 200 OK and the greeting:

w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello:", name)

Note: You can stop the server by pressing CTRL-C

We are returning a string to the calling user (or service). Ideally we may want to return XML or JSON.

Modify the code as follows:

Run the code:

$ go run main.go 

Test it via cURL (or you can use a web browser):

$ curl -X GET http://127.0.0.1:8080/movies -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /movies HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Mon, 06 Jul 2015 21:57:54 GMT
< Content-Length: 168
< 
* Connection #0 to host 127.0.0.1 left intact
{"tt0076759":{"title":"Star Wars: A New Hope","rating":"8.7","year":"1977"},
"tt0082971":{"title":"Indiana Jones: Raiders of the Lost Ark","rating":"8.6","year":"1981"}}

There are a couple things to point out. First, for us to return proper JSON we add Struct tags to the Movie Struct. So for the title we need to add a struct tag like:

`json:"title"`

In the handler we make sure to set our return content-type as: application/json

res.Header().Set("Content-Type", "application/json")

Assuming the JSON data is properly formatted then its returned to the caller. If not, an error will be sent. If an error is sent in this case the content-type will revert to text/plain.

You might also notice even though I didn’t set a status code by default it sent an HTTP 200 OK.

In the movie list there are two movies listed using IMDB keys. What if we only wanted to return just one record? Turns out we can do this easily by borrowing a trick or two from the first example.

Note: The movies variable has become a global variable. For these simple examples this is fine but for an actual service you would store this in a database.

Now you can load a single movie record rather than the whole list at once (even though its only just two items). You supply an IMDB key and if it’s not found you will get an HTTP 404 Not Found back.

Test it via cURL (or you can use a web browser):

$ curl -X GET http://127.0.0.1:8080/movie/tt0082971 -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /movie/tt0082971 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Mon, 06 Jul 2015 23:11:15 GMT
< Content-Length: 79
< 
* Connection #0 to host 127.0.0.1 left intact
{"title":"Indiana Jones: Raiders of the Lost Ark","rating":"8.6","year":"1981"}

And if you attempt to locate an IMDB key that doesn’t exist:

$ curl -X GET http://127.0.0.1:8080/movie/tt0082970 -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /movie/tt0082970 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 404 Not Found
< Content-Type: application/json
< Date: Mon, 06 Jul 2015 23:13:02 GMT
< Content-Length: 19
< 
* Connection #0 to host 127.0.0.1 left intact

Note: There is getting to be some repetitive code that can be removed, I’ll leave that as a task for the eager reader.

So what happens if you want to remove a movie from the list? Let’s add the code now for that.

Run the code:

$ go run main.go 

Remove the record via cURL (or you can use a web browser):

$ curl -X DELETE http://127.0.0.1:8080/movie/tt0082971 -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> DELETE /movie/tt0082971 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 204 No Content
< Content-Type: application/json
< Date: Mon, 06 Jul 2015 23:04:16 GMT
< 
* Connection #0 to host 127.0.0.1 left intact

Verify the record is gone via cURL (or you can use a web browser):

$ curl -X GET http://127.0.0.1:8080/movies -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /movies HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Mon, 06 Jul 2015 23:04:32 GMT
< Content-Length: 76
< 
* Connection #0 to host 127.0.0.1 left intact
{"tt0076759":{"title":"Star Wars: A New Hope","rating":"8.7","year":"1977"}}

The delete call in the code will attempt to remove the record, if its not found it will just do nothing. Our delete call is “idempotent“.

The final thing I’ll do is add the ability to add a new movie record.

Run the code:

$ go run main.go 

Add the new record via cURL:

$ curl -X POST -H "Content-Type: application/json" -d '{"title":"Top Gun","rating":"6.8", "year": "1986"}' http://127.0.0.1:8080/movie/tt0092099 -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /movie/tt0092099 HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> Content-Type: application/json
> Content-Length: 50
> 
* upload completely sent off: 50 out of 50 bytes
< HTTP/1.1 201 Created
< Content-Type: application/json
< Date: Tue, 07 Jul 2015 00:49:06 GMT
< Content-Length: 48
< 
* Connection #0 to host 127.0.0.1 left intact
{"title":"Top Gun","rating":"6.8","year":"1986"}

Verify the record is gone via cURL (or you can use a web browser):

$ curl -X GET http://127.0.0.1:8080/movies -v

Results:

* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /movies HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 127.0.0.1:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: application/json
< Date: Tue, 07 Jul 2015 00:49:55 GMT
< Content-Length: 229
< 
* Connection #0 to host 127.0.0.1 left intact
{"tt0076759":{"title":"Star Wars: A New Hope","rating":"8.7","year":"1977"},
"tt0082971":{"title":"Indiana Jones: Raiders of the Lost Ark","rating":"8.6","year":"1981"},
"tt0092099":{"title":"Top Gun","rating":"6.8","year":"1986"}}

Obviously one more thing you might want is the ability to modify an existing record via a PUT call. I’m not going to add that code but if you want a hint the code for the update will look similar to the POST code.

Post to Twitter

This entry was posted in Go, Open Source. Bookmark the permalink.

3 Responses to Go: Simple, Easy, Fast – Building a Go (golang) REST Service with Gorilla

  1. Pingback: This week in API land, 15th edition | Restlet - We Know About APIs

  2. good tutorial with codes. thanks

  3. Hylson says:

    Very good, thanks!

Comments are closed.