Skip to content

Redis CRUD Operations with JSON Serialization

This guide outlines how to perform basic CRUD (Create, Read, Update, Delete) operations on a Redis database using the Go programming language, utilizing JSON serialization to manage structured data^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

Overview

When building microservices, it is common to need a persistent storage layer that can handle structured data. While file-based storage (like JSON files) is useful for learning, it presents challenges for concurrency and state management. Redis is often used to decouple state from the application^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

In this context, data is stored as serialized JSON strings within Redis keys. This allows complex objects, such as a video struct containing metadata (ID, title, URL, etc.), to be persisted and retrieved efficiently^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

Prerequisites

To interact with Redis in Go, the go-redis client library is required^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

go get github.com/go-redis/redis/v8

You will also need to establish a connection to your Redis instance using a client, typically configured with environment variables for host, port, and password^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

Data Structure

The operations below assume a struct definition representing the entity to be stored. For example, a video struct^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md]:

type video struct {
    Id          string
    Title       string
    Description string
    Imageurl    string
    Url         string
}

Create / Update Operation

Data is stored in Redis by first serializing the Go struct into a JSON byte array, and then saving it using the SET command^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md]. Since Redis keys are unique, using SET with an existing ID effectively acts as an Update operation^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

func saveVideo(video video) {
    // 1. Marshal the struct into JSON
    videoBytes, err := json.Marshal(video)
    if err != nil {
        panic(err)
    }

    // 2. Set the key (video.Id) to the JSON value
    err = redisClient.Set(ctx, video.Id, videoBytes, 0).Err()
    if err != nil {
        panic(err)
    }
}

To save a list of items, iterate over the slice and call the single-record save function for each item^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

Read Operation

Reading data involves retrieving the value for a specific key (usually the entity's ID) from Redis, and then deserializing the resulting JSON string back into a Go struct^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

func getVideo(id string) (video, error) {
    // 1. Get the value from Redis
    val, err := redisClient.Get(ctx, id).Result()

    if err == redis.Nil {
        return video{}, fmt.Errorf("video not found") // Handle empty result
    } else if err != nil {
        return video{}, err // Handle connection errors
    }

    // 2. Unmarshal JSON into the struct
    var v video
    err = json.Unmarshal([]byte(val), &v)
    if err != nil {
        panic(err)
    }

    return v, nil
}

To retrieve all records, you can use the KEYS command to find all matching keys and then iterate over them to fetch each object individually^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

HTTP Handler Integration

These CRUD functions are typically integrated into HTTP handlers to create a RESTful API^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

Get Single or All Videos

A single GET endpoint can handle fetching a specific video by ID or returning all videos if no ID is provided^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

func HandleGetVideos(w http.ResponseWriter, r *http.Request) {
    // Check for 'id' query parameter
    id, ok := r.URL.Query()["id"]

    if ok {
        // Return single video
        video, err := getVideo(id[0])
        if err != nil {
            w.WriteHeader(http.StatusNotFound)
            return
        }
        videoBytes, _ := json.Marshal(video)
        w.Write(videoBytes)
        return
    }

    // Return all videos
    videos := getVideos() // Implementation using KEYS command
    videoBytes, _ := json.Marshal(videos)
    w.Write(videoBytes)
}

Update Videos

The update handler accepts JSON data in the request body, unmarshals it, and saves it to Redis^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md].

func HandleUpdateVideos(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        body, _ := ioutil.ReadAll(r.Body)

        // Check if updating a single video
        _, ok := r.URL.Query()["id"]
        if ok {
            var video video
            json.Unmarshal(body, &video)
            saveVideo(video)
            return
        }

        // Otherwise, update list (bulk)
        var videos []video
        json.Unmarshal(body, &videos)
        saveVideos(videos)
    }
}
  • [[流程化筆記]]: Documenting the specific steps of connecting to the database and handling serialization prevents errors during implementation.
  • 20/80學習原則: Learning the basic SET and GET commands (the 20%) covers the majority of simple use cases for Redis.
  • [[23种经典设计模式]]: The Repository pattern is often used to encapsulate the data access logic shown here.

Sources

^[400-devops__09-Scripting-Language__golang__introduction__part-5.database.redis__readme.md]