More Go RPC, using gorilla/rpc/json
Tags: go, rpc, gorilla, json
Published: 2021-07-28 21:49:37

Following on from an earlier [post](https://dev.to/iamelesq/rpc-with-go-what-is-it-p41) about Go and RPC, this time we're looking at `gorilla/rpc` and `gorilla/rpc/json`. One weakness of the prior example in the post linked above was it required both parts, client and server to be written in Go, a tight coupling not immediately apparent to someone learning either Go or RPC. Enter JSON... 

![Jason not JSON](https://i.gifer.com/8zKr.gif)

Using JSON allows us to send a request as JSON and receive a response as JSON thus removing a tight coupling that we had previously. 

## Setup the project

OK let's get started. You're going to need a project structure much like below, obviously the `modules` files are optional if you're a `GOPATH` user and you don't strictly need a `readme` but you should by force of habit include one in your projects.

```
gorilla-jsonrpc-demo
├── go.mod
├── go.sum
├── projects.json
├── readme.md
└── server
    └── main.go
```

`go get github.com/gorilla/rpc`
`go get -u github.com/gorilla/mux`


### The json data file

Here I've created a dummy data file with some sample projects structures in it. Extremely trivial to avoid complicating the demo. 

```json
[
    {
        "name": "go/golang",
        "author": "google",
        "stars": "88200",
        "forks": "12900"
    },
    {
        "name": "chi",
        "author": "go-chi",
        "stars": "9800",
        "forks": "665"
    },
    {
        "name": "go-me",
        "author": "me",
        "stars": "1",
        "forks": "0"
    },
    {
        "name": "go-you",
        "author": "you",
        "stars": "100",
        "forks": "10"
    },
    {
        "name": "go-them",
        "author": "them",
        "stars": "2000",
        "forks": "112"
    }
]
```
You can adapt your file, structures to suit your case or just to play around with more complex structures. 

### Implementing the RPC server

We're going to make a couple of changes to the previous server we wrote and although the changes are slight they make a world difference. 

```go
package main

import (
	jsonparse "encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/gorilla/mux"
	"github.com/gorilla/rpc"
	"github.com/gorilla/rpc/json"
)

type Args struct {
	Name string
}

type Repo struct {
	Name   string `json:"name,omitempty"`
	Author string `json:"author,omitempty"`
	Forks  string `json:"forks,omitempty"`
	Stars  string `json:"stars,omitempty"`
}

type JSONServer struct{}

func (t *JSONServer) GiveRepoDetails(r *http.Request, args *Args, reply *Repo) error {
	var repos []Repo

	// get the datafile
	absPath, _ := filepath.Abs("projects.json")

	raw, readerr := ioutil.ReadFile(absPath)
	if readerr != nil {
		log.Println("error:", readerr)
		os.Exit(1)
	}

	// unmarshal the raw data from the file into our array for the repos
	marshalerr := jsonparse.Unmarshal(raw, &repos)
	if marshalerr != nil {
		log.Println("error:", marshalerr)
		os.Exit(1)
	}

	for _, repo := range repos {
		if repo.Name == args.Name {
			// we found a hit
			fmt.Println(repo)
			*reply = repo
			break
		}
	}
	return nil
}

func main() {

	port := ":8080"

	s := rpc.NewServer()                                 // Create a new RPC server
	s.RegisterCodec(json.NewCodec(), "application/json") // Register the type of data requested as JSON
	s.RegisterService(new(JSONServer), "")               // Register the service by creating a new JSON server

	r := mux.NewRouter()
	r.Handle("/rpc", s)
	log.Fatal(http.ListenAndServe(port, r))

}
```
Things to note: 
- we've had to rename the standard `encoding/json` to `jsonparse` to avoid clashing with the `gorilla/rpc` json library in our code.
- we're using a gorilla mux
- we are registering a codec for application/json
- we register the service
- in the service we're `unmarshaling` a file into an array
- our results are sent back in JSON format.

## Testing our server

So, how do we run this? Where is the client application this time? Of course we could create one but we can just as easily demo it with a fairly simple curl request.

```curl
curl -X POST \
   http://localhost:8080/rpc \
   -H 'cache-control: no-cache' \
   -H 'content-type: application/json' \
   -d '{
   "method": "JSONServer.GiveRepoDetails",
   "params": [{
   "name": "go-me"
   }],"id": "1"}'
```

Things to note about the `curl` request:
- To get a response you need an ID, I too learned this the hard way after way too much time thinking where is my response? Why is there no output? Thanks to [this post](https://stackoverflow.com/questions/17452199/no-response-from-gorilla-rpc-json-rpc-service) on SO I resolved my issue by adding the `"id": "1"` section.

![victory](https://media2.giphy.com/media/3owzW2qI07rokGALfy/giphy.gif)

I hope this has been simple enough for you get an easy takeaway from, about why and how you might use the `gorilla` libs to help you knock up an RPC json solution. 

Best wishes.