Building a Leaderboard API with Go-Redis and Gin

What is Redis?

Redis is an in-memory data store that can be used as a database, cache, or message broker. In this article, we’ll explore how to use Redis with the Go-redis client library to build a leaderboard API.

Getting Started

To follow along, you’ll need:

  • A Go installation with modules support
  • Redis installed on your local computer (or use the Docker image if you have Docker installed)
  • Experience writing Go

Create a new folder for the project and initialize your Go module. Install the required dependencies, including Gin and Go-redis, with the following commands:

Familiarizing Yourself with Go-Redis

Go-redis provides a type-safe Redis client library for Go with support for features like Pub/Sub, sentinel, and pipelining. To connect to a Redis database, you’ll need to create a new client using the redis.NewClient function. This client is thread-safe and can be shared by multiple goroutines.

Creating the Redis Client

Create a new file called db.go in the db folder and add the following code to create a Redis client:

“`go
package db

import (
“context”
“github.com/go-redis/redis/v8”
)

type Database struct {
client *redis.Client
}

var ErrNil = redis.Nil
var Ctx = context.TODO()

func NewDatabase() (*Database, error) {
client := redis.NewClient(&redis.Options{
Addr: “localhost:6379”,
Password: “”,
DB: 0,
})

if err := client.Ping(Ctx).Err(); err!= nil {
    return nil, err
}

return &Database{client: client}, nil

}
“`

Building the Leaderboard API

Create a new file called main.go and add the following code to create the leaderboard API:

“`go
package main

import (
“github.com/gin-gonic/gin”
“leaderboard/db”
)

func main() {
r := gin.Default()
database, err := db.NewDatabase()
if err!= nil {
log.Fatal(err)
}

initRouter(r, database)
r.Run(":8080")

}
“`

Using Pipelines to Add Users to the Leaderboard

Create a new file called user.go in the db folder and add the following code to use pipelines to add users to the leaderboard:

“`go
package db

import (
“context”
“github.com/go-redis/redis/v8”
)

type User struct {
Username string json:"username"
Score int json:"score"
Rank int json:"rank"
}

func (d *Database) SaveUser(ctx context.Context, user *User) error {
txPipeline := d.client.TxPipeline()
txPipeline.ZAdd(ctx, “leaderboard”, &redis.Z{Score: float64(user.Score), Member: user.Username})
txPipeline.ZRank(ctx, “leaderboard”, user.Username)
_, err := txPipeline.Exec(ctx)
return err
}
“`

Fetching Users’ Scores and Rankings

Add the following code to user.go to fetch a user’s score and ranking:

go
func (d *Database) GetUser(ctx context.Context, username string) (*User, error) {
txPipeline := d.client.TxPipeline()
txPipeline.ZScore(ctx, "leaderboard", username)
txPipeline.ZRank(ctx, "leaderboard", username)
cmds, err := txPipeline.Exec(ctx)
if err!= nil {
return nil, err
}
score, _ := cmds[0].Result()
rank, _ := cmds[1].Result()
return &User{Username: username, Score: int(score.(float64)), Rank: int(rank.(int64))}, nil
}

Fetching the Complete Leaderboard

Create a new file called leaderboard.go in the db folder and add the following code to fetch the complete leaderboard:

“`go
package db

import (
“context”
“github.com/go-redis/redis/v8”
)

const leaderboardKey = “leaderboard”

func (d Database) GetLeaderboard(ctx context.Context) ([]User, error) {
scores, err := d.client.ZRangeWithScores(ctx, leaderboardKey, 0, -1).Result()
if err!= nil {
return nil, err
}
users := make([]*User, len(scores))
for i, score := range scores {
users[i] = &User{Username: score.Member.(string), Score: int(score.Score)}
}
return users, nil
}
“`

Running the Application

To run the application, ensure that you have Redis installed and running. You can then build and run the main.go file with the following commands:


go build main.go
go run main.go

You can test the API using cURL or your favorite API client. Here are some sample cURL commands and their responses:

“`
curl -X GET ‘http://localhost:8080/points/john’
Response: {“username”:”john”,”score”:10,”rank”:1}

curl -X POST -H “Content-Type: application/json” -d ‘{“username”:”jane”,”score”:20}’ http://localhost:8080/points
Response: {“username”:”jane”,”score”:20,”rank”:1}

curl -X GET ‘http://localhost:8080/leaderboard’
Response: [{“username”:”jane”,”score”:20,”rank”:1},{“username”:”john”,”score”:10,”rank”:2}]
“`

Leave a Reply

Your email address will not be published. Required fields are marked *