Building a RESTful API with Go (Part 1)

John Eckert
Published in
10 min readMay 4, 2018

--

Last week I decided that I wanted to learn more about Go. Since it was lunch time, I figured I’d use Go to write a simple backend API for an app that lists a bunch of different sushi rolls and their ingredients. It seemed like it would be an excellent way to get used to the syntax of the language and also learn how to use it in a practical application. My first step was to watch a bunch of tutorials on youtube.

Step 0 — Setup Your Environment

Before doing anything, we need to have Go installed on our machine. I’m not going to go into detail about that here. If like me, you are starting from zero, you can check out the documentation or this tutorial which I found helpful. It’s pretty easy, the only significant difference with Go is that all of your projects reside in the same folder on your computer which is called your Go Workspace.

Step One — Basic Structure

At the top, we have the package declaration. In this case, we are using main which tells the compiler that this will be an executable file. If we were making a library, we would declare a name for it here and this would be the package name we would use whenever we accessed the functions later. For example, if we wrote package maki and then wrote a function called AddWasabi(), we would call that function with maki.AddWasabi().

Next, we have the import keyword. This is where we list all of the libraries that we will use. The libraries’ names will go inside the parentheses as strings. For example, if we needed our AddWasabi() function, we would need to put "maki" here.

Finally, our main() function. Since this will be an executable file, main() is what will get called when our program runs. In other words, this is where the magic happens!

Step Two — Let’s Make a Model

Since we want to be able to render our data as json, we need to add "encoding/json" to our import statement.

Next, we need to create a model to represent our different sushi rolls. We will do this above our main function, and we will use a struct. A struct is used for Object Oriented programming in Go. It’s a user-defined data type that contains a collection of fields. It’s kind of like a class in ES6, in that it can have methods and properties that we define. To define a struct, we use the type keyword, an identifier for our new data type, and the struct keyword followed by curly braces containing the desired named properties.

For my sushi app, I created a struct named Roll with four properties: ID, ImageNumber, Name, and Ingredients. For simplicity’s sake, I made each property be of type string. We define a property by writing it’s identifier, then it’s data type, and finally it’s json identifier wrapped in back ticks.

Since we won’t be using a database for this little exercise, we need a variable to hold all of our sushi rolls. For that, we will use a data type called a slice.

This was a new concept for me. In Go, arrays have a specified length that cannot be changed, but there is another data type called a slice which is like an array but is of variable length.

We create our slice using the var keyword followed by the name of the variable. To specify that our new variable is a slice we use square brackets followed by the data type our slice will contain (in this case instances of our struct Roll.) Now we have a place to hold all that delicious sushi!

Step Three — Implementing the Router

Ok, time to start building our routes. Before we dive in, we are going to need to get a 3rd party library called mux. To install 3rd party libraries, we use the go get command. So the first thing we need to do is run the following line in terminal.

go get -u github.com/gorilla/mux

If you look in your go path in Go/src/github.com/ you will see your new library fresh from the internet. Let’s add it to our import. To add 3rd party libraries we need to specify their paths starting inside the src folder. Also, while we’re there let’s add some more standard libraries:

  • "net/http" which has some client/server functionality we will need.
  • "log" we will use this for a little error handling.

Now that we have all the libraries we need let’s make our router. This time we will be writing in the main function since this code will be executed whenever we run our executable file.

It’s time to use that mux library we downloaded. The first thing we need to do is initialize our router, which I’m just going to call router. Mux has a function for this that we can call and assign the return value to a variable.

router := mux.NewRouter()

If you are new to Go like I am, you might be wondering about that := symbol. It’s pretty awesome, so I’m going to take a second and explain it. When you are inside a function in Go, you can use what is called short variable declaration by skipping the var keyword and using :=. When this syntax is used, Go will infer the variable type based on the value we are giving it.

Time to define our routes. We will create each endpoint by chaining a couple of methods onto our router.

  • .HandleFunc() which takes two arguments, a string that defines our route and a function that handles that route.
  • .Method() which takes an argument of the http method.

If we want full CRUD for our sushi, we will need the following:

They won’t work yet since we haven’t written any of the handler functions, but it looks good so far! Before we write those functions, let’s set up the code to run our server. We will use the ListenAndServe() function from the net/http library. It takes two arguments, an address and a handler. For this little example, our address will be whatever port we want and for the handler, we will pass it the router variable we just defined.

http.ListenAndServe(":5000", router)

That looks great, but let’s do one last thing before we move on. Remember that log library we imported? Let’s wrap this whole line in log.Fatal() so it will throw an error if it fails.

log.Fatal(http.ListenAndServe(":5000", handler))

To test what we have so far, let’s create some empty function definitions for our route handling functions. Each of these functions needs to take two arguments a response w, and a request r. These use special data types from the http library.

Now we can compile our program and test it. Save the file and in terminal, compile it with go build. Now if you look in the directory, you should see an executable file with the same name as the directory mine is sushiAPI. We will run that file from the command line to start our server.

./sushiAPI

Once you run your executable file, your terminal should hang because the server is running. Now if I go to localhost:5000/sushi in Postman, I see… nothing. That’s ok, it’s because there’s nothing in the controller function. However, if I check the headers I can see a status code of 200!

Step 4 — Controller Methods

So far so good, now let’s make it do something. We need two more packages math/rand to generate random numbers strconv so that we can convert strings to other data types. Next, we will work through each of our functions one at a time.

getRolls()

This function is pretty easy to figure out. The first thing we do is set the headers on the response. Then we use the NewEncoder() and Encode() methods to render our rolls slice as json and send it to the response stream. Now our index page should be operational. At least, it would be if we had any data! Let’s add a dummy roll to our rolls slice by dropping this line at the top of our main function (We’ll look more closely at it later.)

rolls = append(rolls, Roll{ID: "1", ImageNumber: "8", Name: "Spicy Tuna Roll", Ingredients: "Tuna, Chili sauce, Nori, Rice"})

Now let’s re-compile, restart our server, and check out our index endpoint in Postman.

Nice!!!

getRoll()

Ok, let’s look at the show function now. Right away we can see some similarities to the index function. We already know what that first line does.

The second line looks kind of new, but there is some familiar stuff here. We can tell we are using := to set params, so it’s pretty safe to assume that the mux.Vars() function is setting our params variable from the http response we are passing it.

Next, we have a for loop, which makes sense because we are going to have to iterate over our Rolls variable to find the one we want. The syntax looked a little weird to me the first time I saw it, so let’s break it down a little.

In Go, for is the only looping construct that exists, so there are multiple ways to use it. We can use it in the familiar way of initializing a counter, setting a condition and then incrementing.

for i := 0; i < 3; i++ {
fmt.Println(i)
}

We can also use it like we would a while loop in JavaScript, by only supplying it with a condition. This will continue to execute the block until the condition is no longer true.

for x > y {
fmt.Println(x)
}

Also, we can supply it with a range clause which allows it to iterate over a collection of data, our slice for example.

for i, element := range myArr {
fmt.Println(element)
}

If we break the range clause down, it consists of a variable to hold the index of the current element, an identifier for the current element, our old friend :=, the range keyword, and the name of the variable that we are iterating over. Our code refines this one step further. Since we aren’t using the index variable, it can be omitted by putting an underscore in its place.

Ok, now that we are Go looping masters, let’s look inside the loop. That if statement is no big deal, when we find the item in our slice where the ID matches the id being sent through our params variable we should render it as json just like we did in the getRolls() function and then return.

Now if we check our show route in postman we see the single roll, just like we’d expect.

createRoll()

So far so good, now let’s figure out how to create a roll. This is our first POST request, so now we get to figure out how to deal with incoming data.

The first thing we need to do is create an instance of our Roll struct, as you can see above I’ve called it newRoll.

Now let’s get some data from the HTTP request and put it in there! To get the data out of our response, we need to head back to the json library that we imported. Much like we’ve been using .NewEncoder() and .Encode() to send our response; we will use .NewDecoder() and .Decode() to read data from our requests. We call json.NewDecoder() and pass it the body of our HTTP request. The decoder reads the data, and we call .Decode() on that passing it a pointer to our newRoll struct which allows it to match the json to the appropriate properties of the struct.

Then to simulate the database we assign it an id, by getting the length of the rolls slice and then calling strconv.Itoa() on it which converts it to a string.

Now that we have our newRoll assembled we need to add it to our slice. To do that we use append(). Append takes at least two arguments, the first is the slice we want to add items to and then we can add multiple arguments of the correct data type to add them to the slice.

Finally, we send a response back containing our new roll, just like before.

I hope you find this walkthrough useful. In future posts, I will go into writing the update and delete functions to round out our CRUD functionality and then maybe I’ll explore how to talk to a database.

--

--