DEV Community

Cover image for Storage Layer 📦
Јане Џумеркоски
Јане Џумеркоски

Posted on

10

Storage Layer 📦

In this part, we will be working on building the storage layer of our short URL application, so let's start!

📝 If Redis is not yet installed on your computer, you can do it following the instructions for installation with respect to your operating system.

First thing first, we will install Redis client for Golang

go get github.com/go-redis/redis/v9
Enter fullscreen mode Exit fullscreen mode

then we create the store folder in the project, and two empty Go files: store.go and store_test.go

├── go.mod
├── go.sum
├── main.go
└── shortener
   ├── shortener.go
   └── shortener_test.go
└── store
   ├── store.go
   └── store_test.go
Enter fullscreen mode Exit fullscreen mode

Now we can define wrapper structs and initialize the store service, in this case our Redis client.

package store

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v9"
    "time"
)

// StorageService is struct wrapper around raw Redis client
type StorageService struct {
    redisClient *redis.Client
}

// Top level declarations for the storeService and Redis context
var (
    storeService = &StorageService{}
    ctx          = context.Background()
)

const CacheDuration = 6 * time.Hour

// InitializeStore is initializing the store service and return a store pointer
func InitializeStore() *StorageService {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    pong, err := rdb.Ping(ctx).Result()
    if err != nil {
        panic(fmt.Sprintf("Error init Redis: %v", err))
    }

    fmt.Printf("\nRedis started successfully: pong message = {%s}", pong)
    storeService.redisClient = rdb
    return storeService
}
Enter fullscreen mode Exit fullscreen mode

Now that our data store service has successfully been initialized, it's time to consider what storage API to offer for our shortener server.

  1. We want to be able to save the mapping between the original URL and the generated short URL.
  2. We should be able to retrieve the initial long URL once the short is provided.

As the next step let's update store.go and implement our storage API.

func SaveURLInRedis(shortURL, originalURL string) {
    err := storeService.redisClient.Set(ctx, shortURL, originalURL, CacheDuration).Err()
    if err != nil {
        panic(fmt.Sprintf("Failed SaveURLInRedis | Error: %v - shortURL: %s - originalURL: %s\n", 
            err, shortURL, originalURL))
    }
}

func RetrieveInitialURLFromRedis(shortURL string) string {
    result, err := storeService.redisClient.Get(ctx, shortURL).Result()
    if err != nil {
        panic(fmt.Sprintf("Failed RetrieveInitialURLFromRedis | Error: %v - shortURL: %s\n", 
            err, shortURL))
    }
    return result
}
Enter fullscreen mode Exit fullscreen mode

It was fairly straightforward. Great job!

Since we already created store_test.go file, we will set up the test shell first and then write unit test for the storage APIs.

package store

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

var testStoreService = &StorageService{}

func init() {
    testStoreService = InitializeStore()
}

func TestStoreInit(t *testing.T) {
    assert.True(t, testStoreService.redisClient != nil)
}

func TestInsertionAndRetrieval(t *testing.T) {
    initialLink := "https://go.dev/doc/tutorial/getting-started"
    shortURL := "dysg5Fas"

    // Persist data mapping
    SaveURLInRedis(shortURL, initialLink)

    // Retrieve initial URL
    retrievedUrl := RetrieveInitialURLFromRedis(shortURL)
    assert.Equal(t, initialLink, retrievedUrl)
}
Enter fullscreen mode Exit fullscreen mode

📝 Don't forget to run go test ./... to check if all your tests work as expected (✅).

As our storage service is set up, we will expose a Rest API endpoint for encoding and decoding the URLs in the next part.


Originally published at projectex.dev

API Trace View

Struggling with slow API calls? 🕒

Dan Mindru walks through how he used Sentry's new Trace View feature to shave off 22.3 seconds from an API call.

Get a practical walkthrough of how to identify bottlenecks, split tasks into multiple parallel tasks, identify slow AI model calls, and more.

Read more →

Top comments (0)

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay