Using Riak with golang

David
4 min readMar 1, 2023

--

In this blog post, we will demonstrate how to integrate Riak KV with a simple Gin HTTP API using Docker Compose. We will create an API that allows users to create their favorite pizza and retrieve it.

Riak KV is a distributed NoSQL key-value data store designed to provide high availability, fault tolerance, and scalability. It is an open-source database that is used to store and retrieve data from a distributed environment. Gin is a popular Golang web framework that provides a fast and minimal HTTP server.

Prerequisites

Before proceeding, make sure you have the following software installed on your machine:

Set up the project structure

Create a new directory named pizza-api and create a new file named docker-compose.yaml in the directory. Create another directory named src in the pizza-api directory. In the src directory, create a new directory named “pizza” and create two new files in it: main.go and handler.go. The project structure should look like this:

pizza-api/
├── docker-compose.yaml
└── src/
└── pizza/
├── handler.go
└── main.go

Create a Dockerfile for the Golang app

Create a new file named “Dockerfile” in the “pizza-api/src/pizza” directory with the following contents:

FROM golang:alpine

RUN apk update && apk add --no-cache git

WORKDIR /app

COPY . .

RUN go mod download

RUN go build -o main .

EXPOSE 8080

CMD ["/app/main"]

Note: This Dockerfile could be optimised to use multi-stage before heading to production.

This Dockerfile uses the Alpine Linux-based Golang image, installs Git to download dependencies, sets the working directory, copies the application code, downloads the dependencies, builds the application, exposes port 8080, and starts the application.

Define the Riak KV Docker container

Add the following service definition to the docker-compose.yaml file:

version: "3.8"
services:
riak:
image: "basho/riak-kv:latest"
environment:
- "RIAK_AUTOMATIC_CLUSTERING=true"
- "RIAK_SEARCH_ON=true"
ports:
- "8087:8087"
- "8098:8098"

This will download the latest Riak KV image from Docker Hub, set the environment variables to enable automatic clustering and search, and expose ports 8087 and 8098 for client and HTTP traffic, respectively.

Implement the Gin HTTP API

Open the main.go file and add the following code:

package main

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()

r.POST("/pizza", createPizza)
r.GET("/pizza/:id", getPizza)

if err := r.Run(":8080"); err != nil {
panic(err)
}
}

func createPizza(c *gin.Context) {
// TODO: Implement creating pizza in Riak KV

c.JSON(http.StatusCreated, gin.H{"message": "Pizza created"})
}

func getPizza(c *gin.Context) {
id := c.Param("id")

// TODO: Implement retrieving pizza from Riak KV

c.JSON(http.StatusOK, gin.H{
"id": id,
"name": "Margherita",
"crust": "Thin",
"size": "Medium",
})
}

This code defines two endpoints: /pizza for creating a new pizza and /pizza/:id for retrieving a pizza by its ID. The implementation for creating and retrieving pizzas will be added later in this tutorial.

Connect to Riak KV

Open the handler.go file and add the following code:

package pizza

import (
"github.com/basho/riak-go-client"
)

var client *riak.Client

func init() {
var err error

client, err = riak.NewClient(&riak.ClientConfig{
Address: "riak:8087",
})
if err != nil {
panic(err)
}
}

This code initializes a Riak KV client and connects to the riak container using the default port (8087).

Implement creating pizzas in Riak KV

Replace the TODO comment in the createPizza function with the following code:

func createPizza(c *gin.Context) {
var pizza Pizza
if err := c.ShouldBindJSON(&pizza); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

key := riak.NewBinaryValue(pizza.ID)
obj := &riak.Object{
Bucket: "pizzas",
Key: key,
ContentType: "application/json",
Value: []byte(pizza.String()),
}

if _, err := client.StoreObject(obj); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusCreated, gin.H{"message": "Pizza created"})
}

This code uses the ShouldBindJSON method to deserialize the JSON payload into a Pizza struct. It then creates a new riak.BinaryValue with the pizza's ID and creates a new riak.Object with the pizza's ID as the key, the pizzas bucket, and the JSON payload as the value. Finally, it stores the object in Riak KV using the client.StoreObject method.

Implement retrieving pizzas from Riak KV

Replace the TODO comment in the getPizza function with the following code:

func getPizza(c *gin.Context) {
id := c.Param("id")
key := riak.NewBinaryValue(id)

obj, err := client.FetchObject(&riak.FetchOptions{
Bucket: "pizzas",
Key: key,
})
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Pizza not found"})
return
}

var pizza Pizza
if err := obj.GetValue(&pizza); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, pizza)
}

This code retrieves the pizza’s ID from the URL parameter, creates a new riak.BinaryValue with the pizza's ID as the key, and fetches the object from Riak KV using the client.FetchObject method. If the object is not found, it returns a 404 error. Otherwise, it deserializes the object's value into a Pizza struct and returns it as a JSON response.

Run the application

Open a terminal window, navigate to the pizza-api directory, and run the following command:

docker-compose up

This will start the Riak KV and Golang containers and expose the API on port 8080.

Test the API

Open a new terminal window and use the following commands to test the API:

# Create a new pizza
curl -X POST -H "Content-Type: application/json" -d '{"id": "1", "name": "Margherita", "crust": "Thin", "size": "Medium"}' http://localhost:8080/pizza
# Retrieve the pizza by ID
curl http://localhost:8080/pizza/1

The first command creates a new pizza with the ID 1, the name Margherita, the crust Thin, and the size Medium. The second command retrieves the pizza by its ID and returns its details as a JSON response.

Conclusion

In this blog post, we demonstrated how to integrate Riak KV with a simple Gin HTTP API using Docker Compose. We created an API that allows users to create their favorite pizza and retrieve it. We used the `riak-go-client` library to connect to Riak KV, store and retrieve objects, and demonstrated how to use the library in a Gin handler. By following this tutorial, you can now create your own distributed, fault-tolerant APIs using Riak KV and Gin.

--

--

David

Coding is both my hobby and my job. I love writing about things I'm working on ❤️