Skip to content

2. Using Redis DB: key value

Mahir edited this page Jul 23, 2022 · 4 revisions

Important: Make sure Redis server is installed on the machine.

Final result:

Endpoints:

Add a new country: [HTTP POST] http://localhost:1200/api/v1/new-country

Body:

{
  "countryName":"",
  "countryUNcode":""
}

Find a country: [HTTP GET] http://localhost:1200/api/v1/search-country/:countryName

Create a project, build, run

This is a continuation of 1. RESTful API: Hello world project.

  • Create a file countries.go inside controller folder
  • Write a function AddCountry to countries.go file
  • Write a function FindCountry to countries.go file
package controller

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/mediocregopher/radix/v4"

	"github.com/pilinux/gorest/database"
	"github.com/pilinux/gorest/lib/renderer"
)

// CountryCode - key:value
type CountryCode struct {
	CountryName   string `json:"countryName"`
	CountryUNcode string `json:"countryUNcode"`
}

// AddCountry - SET key
func AddCountry(c *gin.Context) {
	data := CountryCode{}
	if err := c.ShouldBindJSON(&data); err != nil {
		renderer.Render(c, gin.H{"msg": "bad request"}, http.StatusBadRequest)
		return
	}

	data.CountryName = strings.TrimSpace(data.CountryName)
	data.CountryUNcode = strings.TrimSpace(data.CountryUNcode)
	if len(data.CountryName) < 4 {
		renderer.Render(c, gin.H{"msg": "requires a valid country name"}, http.StatusBadRequest)
		return
	}
	if len(data.CountryUNcode) < 3 {
		renderer.Render(c, gin.H{"msg": "requires a valid country UN code (3-digit)"}, http.StatusBadRequest)
		return
	}

	client := database.GetRedis()

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(database.RedisConnTTL)*time.Second)
	defer cancel()

	// Set key in Redis
	result := ""
	if err := client.Do(ctx, radix.FlatCmd(&result, "SET", data.CountryName, data.CountryUNcode)); err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}

	if result != "OK" {
		renderer.Render(c, gin.H{"msg": "operation failed"}, http.StatusNotAcceptable)
		return
	}

	renderer.Render(c, data, http.StatusOK)
}

// FindCountry - GET key
func FindCountry(c *gin.Context) {
	countryName := c.Params.ByName("country-name")
	countryName = strings.TrimSpace(countryName)
	if len(countryName) < 4 {
		renderer.Render(c, gin.H{"msg": "requires a valid country name"}, http.StatusNotFound)
		return
	}

	data := CountryCode{}
	data.CountryName = countryName
	client := database.GetRedis()

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(database.RedisConnTTL)*time.Second)
	defer cancel()

	// Is key available in Redis
	result := 0
	if err := client.Do(ctx, radix.FlatCmd(&result, "EXISTS", data.CountryName)); err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}
	if result == 0 {
		renderer.Render(c, gin.H{"msg": "country does not exist in the database"}, http.StatusNotFound)
		return
	}

	// Find key in Redis
	if err := client.Do(ctx, radix.FlatCmd(&data.CountryUNcode, "GET", data.CountryName)); err != nil {
		renderer.Render(c, gin.H{"msg": "internal server error"}, http.StatusInternalServerError)
		return
	}

	renderer.Render(c, data, http.StatusOK)
}

  • Update the main function in main.go file
package main

import (
	"fmt"

	"myapi/controller"

	"github.com/gin-gonic/gin"
	"github.com/pilinux/gorest/config"
	"github.com/pilinux/gorest/database"
	"github.com/pilinux/gorest/lib/middleware"
)

var configure = config.Config()

func main() {
	if configure.Database.REDIS.Activate == "yes" {
		// Initialize REDIS client
		if _, err := database.InitRedis(); err != nil {
			fmt.Println(err)
			return
		}
	}

	router, err := SetupRouter()
	if err != nil {
		fmt.Println(err)
		return
	}
	err = router.Run(":" + configure.Server.ServerPort)
	if err != nil {
		fmt.Println(err)
		return
	}
}

// SetupRouter ...
func SetupRouter() (*gin.Engine, error) {
	router := gin.Default()

	router.Use(middleware.CORS(
		configure.Security.CORS.Origin,
		configure.Security.CORS.Credentials,
		configure.Security.CORS.Headers,
		configure.Security.CORS.Methods,
		configure.Security.CORS.MaxAge,
	))
	// For gorest <= v1.4.5
	// router.Use(middleware.CORS())

	// API:v1
	v1 := router.Group("/api/v1/")
	{
		v1.GET("greetings", controller.Greetings)
		if configure.Database.REDIS.Activate == "yes" {
			v1.POST("new-country", controller.AddCountry)
			v1.GET("search-country/:country-name", controller.FindCountry)
		}
	}

	return router, nil
}

  • Rename the file from .env.sample to .env and modify the following lines:
APP_PORT=1200
ACTIVATE_REDIS=yes

  • Build and start the app

go mod tidy -compat=1.17

go build

chmod +x myapi

./myapi

Project file structure

myapi
│--- main.go
│--- go.mod
│--- go.sum
│--- .env
│
└─── controller
│    └--- greetings.go
│    └--- countries.go

More examples:

controller/playground.go