REST services with Mux and Go

Mux is a “powerful URL router and dispatcher” for Go and that means it allows us to route REST style requests to produce REST/micro services.

Creating our service

Let’s start out by creating a very simple EchoService which will contain our service implementation.

In my code based I create a service folder and add a service.go file, here’s the code

Note: This creation of a new go file, interface etc. is a little over the top to demonstrate mux routes/handlers, but my OO roots are probably showing through. In reality we just need a simple function with an expected signature – which we’ll see later.

package service

type IEchoService interface {
	Echo(string) string
}

type EchoService struct {

}

func (EchoService) Echo(value string) (string, error) {
	if value == "" {
		// create an error
		return "", nil
	}
	return value, nil
}

We’ve created an interface to define our service and then a simple implementation which can return a string and an error. For now we’ll not use the error, hence return nil for it.

Before we move onto the Mux code which will allow us to route requests/responses, let’s create a bare bones main.go file

package main

import (
   "goecho/service"
)

func main() {
   svc := service.EchoService{}
}

At this point this code will not compile because we haven’t used svc.

Implementing our router

Before we get started we need to run

go get -u github.com/gorilla/mux

from the shell/command prompt.

Now let’s add the code to create the mux router and to create a server on port 8080 that will be used to connect to our router (and service). We’ll also include code to log any fatal errors (again this code will not compile as the previously created svc variable remains unused at this point)

package main

import (
   "goecho/service"
   "github.com/gorilla/mux"
   "log"
   "net/http"
)

func main() {

   svc := service.EchoService{}

   router := mux.NewRouter()

   // service setup goes here

   log.Fatal(http.ListenAndServe(":8080", router))
}

I think this code is pretty self-explanatory, so we’ll move straight on to the implementation of our route and handler.

I’m going to add the handler to the service package but this handler needn’t be a method on the EchoService and could just be a function in the main.go (as mentioned previously).

You’ll need to add the following imports

import (
   "net/http"
   "github.com/gorilla/mux"
)

and then add this method, which is the handler and will call into our EchoService method.

func (e EchoService) EchoHandler(w http.ResponseWriter, r *http.Request) {
   vars := mux.Vars(r)

   result, _ := e.Echo(vars["s"])

   w.WriteHeader(http.StatusOK)
   w.Write([]byte(result))
}

To allow us to call the EchoService Echo method we declare a variable e for the EchoService. The arguments, of types ResponseWriter and Request are required to decode a request and to allow us to write a response. In this example the mux.Vars will be used to get us part of the rest command/URL.

Again, we’re not bothering (at this point to worry about the errors, so result, _ is used to ignore the error.

Next we write a Status OK code back and write the result back as a byte array.

Obviously we now need to set up our handler in main.go, so replace the line

// service setup goes here

with

router.HandleFunc("/echo/{s}", svc.EchoHandler).Methods("GET")

this simply creates the route for calls onto http://<hostname>/echo/??? (where ??? is any value which gets mapped to the mux.Vars[“s”]) through to the supplied handler (svc.EchoHandler) using the GET method.

For example, navigating to http://localhost:8080/echo/HelloWorld in your preferred web browser should display HelloWorld.

We can add multiple routes/handlers, for example let’s create a handler to respond with “Welcome to the EchoService” if the user navigates to http://localhost:8080. Place this function in main.go

func WelcomeHandler(w http.ResponseWriter, r *http.Request) {
   w.WriteHeader(http.StatusOK)
   w.Write([]byte("Welcome to the EchoService"))
}

and add this handler code before (or after) your existing handler code in main

router.HandleFunc("/", WelcomeHandler).Methods("GET")