Implememt a KVDB Key-Value Database effectively using Interfaces
@ Punit Naran | Monday, Feb 15, 2021 | 4 minutes read | Update at Monday, Feb 15, 2021

Simple (KVDB) Key-Value Database Implementation

For this post I will be showing you how to implement a Database interface. This will be a simple Key-Value Database.

  • The database should have at least a minimum interface for a KVDB
    • Get, Set and Delete operations
    • Close Database connection

First lets create a database.go file. We can call it a database package.

Now we need to implement a Database instance. The code below shows the minimum required calls (operations) needed for a Key-Value Database.


// DBStore is a database instance
type DBStore interface {
  
  // Get returns a value for a given key, or a error response.
  Get(key string) ([]byte, error)

  // Set will store the value in the assosiated key block, or create a key
  // block and store the value. If there is an invalid error of data it should 
  // return a bad request error.
  Set(key string, value []byte) error

  // Delete will delete the value for a given key.
  Delete(key string) error

  // Close will close the database, expect no error if the database has been
  // disconnected, otherwise an error response.
  Close() error

}

// NotFoundError indicates that no value was found for the given key
type NotFoundError struct {
	missingKey string
}

// NewNotFoundError returns a new error for the missing key
func NewNotFoundError(missingKey string) error {
	return &NotFoundError{missingKey}
}

// Error returns missing key error string
func (n *NotFoundError) Error() string {
	return fmt.Sprintf("Could not find value for key: %s", n.missingKey)
}

Now we can select our Key-Value Database. From this interface implementation we could also link an in-memory store database, however for this post we will just focus on using redis.

Assuming that you have created a go file, given the file a package name like redis and imported the go-redis repository.

We can now do the following:

import (
  ...
  // path to your database.go file
  "github.com/punitnaran/example/database"
)

// RedisDatabase wraps the redis client so we can now expose it to
// our application
type RedisDatabase struct {
  Client *redis.Client

  // If you have created a connection or are using a different version of redis 
  // you may want to use the Do command which sends a command to the 
  // server and returns the received reply. Have a look at this as an example:
  // https://pkg.go.dev/github.com/garyburd/redigo/redis#hdr-Executing_Commands
}

var (
  // ErrNil Redis no match response
  ErrNil = errors.New("no matching record found in redis database")

  // Ctx an empty context to use with the client
  Ctx    = context.TODO()
)

// NewRedisDatabase connects the client to the redis server
// returning an redis client instance
func NewRedisDatabase(address string) (*Database, error) {
  // Client connects to redis server with provided options
  client := redis.NewClient(&redis.Options{
      Addr: address,
      Password: "",
      DB: 0,
  })

  // Ping to the connection to check if it is alive
  if err := client.Ping(Ctx).Err(); err != nil {
      return nil, err
  }

  // Return the redis client instance
  return &RedisDatabase{
    Client: client,
  }, nil
}

// Get returns the value from the specifed key otherwise a NotFoundError if the
// key has not been found, or any other error encountered
func (db *RedisDatabase) Get(key string) ([]byte, error) {
  value, err := client.Get(key).Result()
  if err == redis.Nil {
    // Key not found
    return nil, database.NewNotFoundError(key)
  }
  if err != nil {
    // Encountered another error
    return nil, err
  }
  return value, nil
}

// Sut adds value to the database and returns any error encountered
func (db *RedisDatabase) Set(key string, value []byte) error {
  err = client.Set(key, value, 0).Err()
  if err != nil {
    return err
  }
  return nil
}

// Deletes the key and value pair
func (db *RedisDatabase) Delete(key string) error{
   return client.Del(key).Err()
}

// Close closes the client and any open resources
func (db *RedisDatabase) Close() error{
  return db.Close()
}

Referances

Niklas – Implementing a key-value database in Golang

Michael Okoko – How to use Redis as a database with go-redis

Cryptocurrency & Bitcoin donation button by NOWPayments

© 2021 - 2025 Punit Naran

Powered by Hugo with theme Dream.

Punit Naran
“If you want to find the secrets of the universe, think in terms of energy, frequency and vibration.” ~ N. Tesla

About Me

Hello, I’m Punit Naran—a hands-on software engineer with a knack for turning complex challenges into elegant, real-world solutions. Over the years, I’ve had the pleasure of working on a variety of projects that span from cloud infrastructure and DevOps to cutting-edge computer vision and quantum cryptography.

In my professional journey, I’ve taken on roles that have pushed me to innovate and lead. Whether at Deloitte, Snaptrip, InfoSum, or Crypto Quantique, I’ve been immersed in projects that demanded scalable, high-performance systems built primarily in Go. I’ve enjoyed architecting and fine-tuning solutions that streamline processes, enhance client privacy, and leverage the latest in microservices, containerization, and automation.

Beyond the workplace, I love diving into personal projects that fuel my curiosity. For instance, I’ve explored behavior-driven development through a project I call “DockerDogs” that uses TestContainers with GoDogs. My GitHub hosts a variety of experiments—from a quantum-resistant UUID generator to a cryptocurrency trader with smart arbitrage strategies—all crafted with a passion for clean, concise code.

This website is my playground—a space where I share insights, tutorials, and the occasional deep dive into projects that have challenged and inspired me. I invite you to join me on this journey as I continue to explore, learn, and build software that makes a difference.