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}]
“`