Creating an in-memory hash table API

Creating an in-memory hash table API#

We are now starting to make our key-value server. Using our hello world server that returns JSON as a base, we can extend it with all the rest of the pieces a key-value server needs.

Basic handlers Get, Set & Delete#

We can start adding handlers for our basic business logic. We will need three essential functions:

  • Get a value by key

  • Set a key with a value

  • Delete a key

Put this below the existing r.Get("/" ...) section within the main func.

Let's begin with the Get handler. It has three things worth talking about.

First is the URL pattern: "/key/{key}", which is the first argument to the r.Get function. This is like saying "for every request URL that starts with /key/, send it to this handler, with the variable key containing everything after the second slash". The handler in this case is the second argument to r.Get - an anonymous function which matches type of a http.HandlerFunc.

This is a feature of Chi, as Get is a function of Chi's router. Sadly, Go is a very explicit language, so if you're used to Ruby or JavaScript which just might make a magic variable available, this might come as a shock, but in Go, you must ask for the variable. So the first line in the function, we get the data from the request URL and save it to a local variable with key := chi.URLParam(r, "key").

A lot of developers might immediately take this variable and do data validation on this incoming key. This is important, but we will save that for later, because we are just going to call a yet-to-be defined function named Get(), which will do the actual work of getting the data. We do this so we can separate concerns. The web server code is distinct from the key/value storage code.

This Get() function call brings us to the second thing we want to talk about, r.Context(). Context was introduced into Go around version 1.7 in 2014. Context is a concept that carries request-scoped data with it. We could add a timeout or extra tracing data to our program, and with context, it is plumed all the way through. Each function that receives a context is in charge of dealing with it, and also not harming the package-scoped variables shoved in there. Mostly Go deals with this for us, and you'll find most modern Go libraries deal with context correctly. To start with, we won't do anything with the context, but leave it there so we can easily pass through data later.

The third thing to talk about is the error handling. We skipped over this in the JSON function, but we will address it now. The Get function, like many things in Go, returns both data and an error. This tends to confuse many programmers used to languages with throw or raise exception functionality. Go doesn't have that. Instead it passes around an instance of the error type. If it is nil, data is good, and if it is not nil, there was an error and you need to handle it. In this case, we are setting the status code to 500 (which is what the constant http.StatusInternalServerError equals), and then returning a JSON blob to the user with the string inside the error.

That's it, now we have a basic Get handler!

If we look at the Delete handler, which responds to an HTTP DELETE verb instead of the GET verb, we can see it looks almost identical. This should be added after our r.Get handler from above.

Finally, we will need to be able to set values, which we will do with a Set handler, dealing with POST requests, which looks similar, but slightly different, as we will need to parse the POST request body. Put this after the r.Delete handler we just added.

 

This page is a preview of Reliable Webservers with Go

Please select a discussion on the left.