Provider-agnostic storage for Go.
Type-safe CRUD across key-value stores, blob storage, SQL databases, and vector similarity search with a unified interface.
// Same code, different providers
sessions := grub.NewStore[Session](redis.New(client))
sessions := grub.NewStore[Session](badger.New(db))
sessions := grub.NewStore[Session](bolt.New(db, "sessions"))
// Type-safe operations
session, _ := sessions.Get(ctx, "session:abc")
sessions.Set(ctx, "session:xyz", &Session{UserID: "123"}, time.Hour)Swap Redis for BadgerDB. Move from S3 to Azure. Switch databases from SQLite to PostgreSQL. Your business logic stays the same.
// Key-value, blob, SQL, or vector — same patterns
store := grub.NewStore[Config](provider) // key-value
bucket := grub.NewBucket[Document](provider) // blob storage
db := grub.NewDatabase[User](conn, "users", renderer) // SQL
index := grub.NewIndex[Embedding](provider) // vector searchFour storage modes, consistent API, semantic errors across all providers.
go get github.com/zoobz-io/grubRequires Go 1.24+.
package main
import (
"context"
"fmt"
"time"
"github.com/zoobz-io/grub"
"github.com/zoobz-io/grub/redis"
goredis "github.com/redis/go-redis/v9"
)
type Session struct {
UserID string `json:"user_id"`
Token string `json:"token"`
ExpiresAt int64 `json:"expires_at"`
}
func main() {
ctx := context.Background()
// Connect to Redis
client := goredis.NewClient(&goredis.Options{Addr: "localhost:6379"})
defer client.Close()
// Create type-safe store
sessions := grub.NewStore[Session](redis.New(client))
// Store with TTL
session := &Session{
UserID: "user:123",
Token: "abc123",
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
}
_ = sessions.Set(ctx, "session:abc123", session, 24*time.Hour)
// Retrieve
s, _ := sessions.Get(ctx, "session:abc123")
fmt.Println(s.UserID) // user:123
}| Feature | Description | Docs |
|---|---|---|
| Key-Value Store | Sessions, cache, config with optional TTL | Providers |
| Blob Storage | Files and documents with metadata | Lifecycle |
| SQL Database | Structured records with query capabilities | Concepts |
| Vector Search | Similarity search with metadata filtering | Providers |
| Atomic Views | Field-level access for encryption pipelines | Architecture |
| Semantic Errors | ErrNotFound, ErrDuplicate across all providers |
API Reference |
| Custom Codecs | JSON default, Gob available, or bring your own | Concepts |
- Type-safe — Generics eliminate runtime type assertions
- Swap backends — Change providers without touching business logic
- Consistent errors — Same error types whether you're using Redis or S3
- Atomic views — Field-level access for framework internals (encryption, pipelines)
- Isolated dependencies — Each provider is a separate module; only pull what you use
Grub enables a pattern: define storage once, swap implementations freely.
Your domain code works with typed stores. Infrastructure decisions — Redis vs embedded, S3 vs local filesystem, PostgreSQL vs SQLite, Pinecone vs Qdrant — become configuration, not architecture.
// Domain code doesn't know or care about the backend
type SessionStore struct {
store *grub.Store[Session]
}
func (s *SessionStore) Save(ctx context.Context, session *Session) error {
return s.store.Set(ctx, "session:"+session.Token, session, 24*time.Hour)
}
// Production: Redis
store := grub.NewStore[Session](redis.New(redisClient))
// Development: embedded BadgerDB
store := grub.NewStore[Session](badger.New(localDB))
// Testing: in-memory
store := grub.NewStore[Session](badger.New(memDB))One interface. Any backend. Zero vendor lock-in.
- Overview — Design philosophy and architecture
- Quickstart — Get started in minutes
- Core Concepts — Stores, buckets, databases, codecs
- Architecture — Layer model, atomic views, concurrency
- Providers — Setup and configuration for all backends
- Lifecycle — CRUD operations and batch processing
- Pagination — Listing and iterating large datasets
- Testing — Mocks, embedded DBs, testcontainers
- Best Practices — Key design, error handling, performance
- Caching — Cache-aside, read-through, TTL strategies
- Migrations — Switching providers without downtime
- Multi-Tenant — Tenant isolation patterns
See CONTRIBUTING.md for guidelines.
MIT License — see LICENSE for details.