Building a RESTful API with Go (Part 2)

Update and Delete

Thanks for checking out Part 2 of this post. If you missed Part 1, you might want to read that first and then come back.

Last week we left off with our server up and running and our index, create, and show actions working.

This is what we ended up with last week

Let’s finish up those last two functions!

updateRoll()

First, our update method. As usual, we have some of the pieces already, but there are some new things we need to consider.

We start by setting our headers and grabbing our params, which we’ve done before.

The next step is to iterate over our rolls slice and find the roll we are trying to update. Time to bust out our trusty for loop and use the range type, just like we did in the getRoll() function. This time, however, we are going to need access to the index, so instead of _ we need to give it an actual variable, I chose i.

Inside our for loop, we use an if statement to compare each item’s ID to the id value in our params variable. Once we find it, we need to remove it from our rolls slice and replace it with the new version. We know how to append() elements to a slice, but how do we remove an element? It turns out we can use append for that too!

To remove an element from a slice, we need to get a little creative with the append command. Let’s look at the line we wrote.

rolls = append(rolls[:i], rolls[i+1:]...)

We know from our previous uses that the first argument to the append function tells it which object we want to append to and the rest of the objects are the items to be appended. In the past, we have simply appended to rolls, but now we need to get a little more creative.

We can use square bracket notation to dictate which part of the original slice we want to use. To access a piece of a slice, we use the syntax slice[low:high] where low is our starting point, and high is the index where we want to stop. If either of those values are the beginning or the end value, they are omitted. It is important to note that much like the .slice() function in JavaScript, our new slice will not contain the item we pass as the endpoint. For example, rolls[1:3] would return a slice containing the roll at index 1 and the roll at index 2.

So with rolls[:i] we are telling our append function to use our rolls slice up to, but not including, the current index as the base of our new slice. By passing rolls[i+1:]… as the second argument, we are appending each element in the original slice starting at the position after the current item. The ... tells Go that even though we are passing append a slice, we want it to treat each element as a separate argument. If we were to omit the ... it would throw an error because it would try to append the entire slice as a single argument and the type wouldn’t match.

Why does this work?

It turns out that Slices and Arrays are more linked than I previously understood. A Slice isn’t actually an Array with variable size, in truth, it’s more like a viewer that is only showing the relevant part of an underlying array. It is more like a struct that contains a pointer to the element in the array were the slice begins, a value for its current length, and a value for its capacity. The cool thing about this is that creating a slice doesn’t allocate new memory or copy anything, so manipulating the data is relatively cheap!

Hold on! Why don’t we run into errors when adding to slices, like when we add sushi to our rolls slice? It turns out, that append() manages that for us. If we append to our slice in a way that would make the underlying array larger, append() automatically copies the array over, into an array of the new size, for us.

append() is amazing!

Back to the Business!

Now that we have successfully removed the old roll, we need to create the new version and append it. We already wrote that code in our create function. But wait, don’t just copy/paste! In the create function we are generating an ID for the new roll. In this case, we want to use the same ID as the roll we just deleted, which we have access to from our params.

newRoll.ID = params["id"]

Once we have the ID set, we can append it to our rolls slice and respond with the updated roll.

Success!!

deleteRoll()

Last but not least, we have the delete function. We’ve already written all the code we need for this function, so let’s think about what we need to do.

First, we need to grab the id from the request; then we need to iterate over our rolls slice and find the roll with the matching id. Once we have it, all we need to do is remove it from the slice and send a response.

The first couple of lines should be totally routine at this point, we start by setting the header and then we use Mux to set our params variable.

Then we iterate! We can use a for loop just like we did in getRoll() and createRoll().

Inside the for loop, we can use the if statement from updateRoll(). The only difference is that we only need the line that removes the current element.

After that, our work is done, so we use break to exit the loop.

Finally, we can respond with the rolls slice, now missing the roll that we deleted.

Now we have all of our endpoints working successfully. Here is what the code looks like at this point:

We have full CRUD functionality up and running! I plan on continuing to add to this project in future posts. Next time, I will probably tackle connecting to a database. I just need to figure out how to do it first!