New in v2.0: Auto-Indexing with Struct Tags + Declarative Cascade Deletes

Skip the Database. Use Redis + S3 Instead.

Turn Redis (indexes) + S3 (storage) into a queryable, transactional document store. 85% cost savings. Zero database complexity.

Go 1.18+
MIT License
Production Ready
// Simple API - struct tags = instant queries
import "github.com/adrianmcphee/smarterbase/v2/simple"

type User struct {
    ID    string `json:"id" sb:"id"`
    Email string `json:"email" sb:"index"`
    Role  string `json:"role" sb:"index"`
    Name  string `json:"name"`
}

// Zero config - auto-detects from env
db := simple.MustConnect()
defer db.Close()

users := simple.NewCollection[User](db)

// Create with auto-ID
user, _ := users.Create(ctx, &User{
    Email: "alice@example.com",
    Name:  "Alice",
    Role:  "admin",
})

// Query by any indexed field - O(1)
admins, _ := users.Find(ctx, "role", "admin")
alice, _ := users.FindOne(ctx, "email", "alice@example.com")

Instead of This → Do This

Traditional Database

RDS PostgreSQL: $271/month
❌ ALTER TABLE downtime
❌ Database servers to patch
❌ Backup strategies to test
❌ Connection pools to tune
❌ DBA expertise required

SmarterBase

Redis + S3: $36/month
✅ No ALTER TABLE statements
✅ Zero servers (S3 is serverless)
✅ Zero backups (11 9s durability)
✅ Simple key-value (no tuning)
✅ Junior devs can deploy

Why Developers Choose SmarterBase

11 9s Durability

99.999999999% data durability via S3 multi-AZ replication

Self-Healing

Automatic index repair and circuit breaker protection built-in

Zero Backups

S3 handles durability automatically, no backup strategies needed

Schema Evolution

JSON documents with built-in versioning, migrations happen on read

JSONL Support

Append-only event logs, perfect for audit trails and activity logs

Database Features Without the Database

Everything you need for document storage, built on infrastructure you already have

// Secondary indexes via Redis Sets
redisIndexer.RegisterMultiValueIndex(
    "orders", "user_id",
    func(data []byte) (string, string, error) {
        var order Order
        json.Unmarshal(data, &order)
        return order.ID, order.UserID, nil
    },
)

// Query - O(1) lookup
orderIDs, _ := redisIndexer.QueryMultiValueIndex(
    ctx, "orders", "user_id", "user-123",
)
Secondary Indexes

Fast O(1) Lookups via Redis

Indexes stored in Redis Sets. Query by any field with constant-time lookups. Redis is required for indexing.

  • O(1) index queries
  • 1:N relationships (user → orders)
  • Automatic index updates
  • Self-healing with auto-repair
  • Circuit breaker protection
Distributed Locks

Race-Free Updates

Redis distributed locks eliminate S3 race conditions. True atomic operations across multiple servers.

  • Prevents lost updates
  • Automatic retry with backoff
  • TTL protection against deadlocks
  • Production-tested at scale
// Atomic updates with distributed lock
lock := smarterbase.NewDistributedLock(
    redisClient, "smarterbase",
)

smarterbase.WithAtomicUpdate(
    ctx, store, lock,
    "accounts/123", 10*time.Second,
    func(ctx context.Context) error {
        var account Account
        store.GetJSON(ctx, "accounts/123", &account)
        account.Balance += 100
        store.PutJSON(ctx, "accounts/123", &account)
        return nil
    },
)
// Full observability built-in
metrics := smarterbase.NewPrometheusMetrics(
    prometheus.DefaultRegisterer,
)
logger, _ := smarterbase.NewProductionZapLogger()
store := smarterbase.NewStoreWithObservability(
    backend, logger, metrics,
)

// Automatic tracking:
// - Operation latency (histogram)
// - Success/error rates
// - Circuit breaker state
// - Index health & drift
// - Transaction rollback failures
// - Query performance
Observability

Production-Grade Monitoring

Prometheus metrics and structured logging out of the box. Know exactly what's happening in production.

  • 15+ Prometheus metrics
  • Zap structured logging
  • Index health monitoring
  • Transaction error tracking
  • Query profiling
Schema Versioning

Evolve Schemas Without Downtime

Add a version field, register migrations, and your data transforms automatically on read. No ALTER TABLE statements, no downtime.

  • Opt-in with _v field
  • Lazy migration on read
  • Automatic version chaining
  • Zero overhead when not used
  • Gradual write-back policy
// Define version evolution
type UserV0 struct {
    Name  string
    Email string
}

type UserV2 struct {
    V         int    `json:"_v"`
    FirstName string `json:"first_name"`
    LastName  string `json:"last_name"`
    Email     string
}

// Type-safe migration function
func migrateUser(old UserV0) (UserV2, error) {
    parts := strings.Fields(old.Name)
    return UserV2{
        V:         2,
        FirstName: parts[0],
        LastName:  strings.Join(parts[1:], " "),
        Email:     old.Email,
    }, nil
}

// Register with zero boilerplate
smarterbase.WithTypeSafe(
    smarterbase.Migrate("UserV2").From(0).To(2),
    migrateUser,
)

// Old data migrates automatically
var user UserV2
user.V = 2
store.GetJSON(ctx, "users/123", &user)

Perfect for "No Database" Applications

If you don't need JOINs or complex aggregations, you probably don't need a database

User Management

Store user profiles, preferences, sessions. No schema migrations when adding new fields.

E-commerce

Orders, invoices, product catalogs. Index by user, status, date without complex queries.

Configuration

App configs, feature flags, tenant settings. Update instantly without migrations.

Content Management

Blog posts, articles, documentation. Store rich JSON without rigid schemas.

Event Logging

Audit trails, activity logs. Append-only JSONL format for efficient writes.

Multi-tenant SaaS

Per-tenant configuration and data. Scale without database partitioning complexity.

Cost Comparison

85% cheaper than managed databases (1TB data, 1M requests/day)

SmarterBase

$41 /month

S3: $23
Requests: $5
Redis: $13

RDS PostgreSQL

$271 /month

Instance: $41
Storage: $115
Backups: $115

DynamoDB

$300 /month

Storage: $250
Read/Write: $50+

MongoDB Atlas

$100 /month

M10 cluster: $57
Storage: $43+

Quick Start

Get started in minutes with Go 1.18+

1

Installation

go get github.com/adrianmcphee/smarterbase/v2
2

Simple API - Quick Start

package main

import (
    "context"
    "github.com/adrianmcphee/smarterbase/v2/simple"
)

type User struct {
    ID    string `json:"id" sb:"id"`
    Email string `json:"email" sb:"index"`
    Name  string `json:"name"`
}

func main() {
    ctx := context.Background()

    // Auto-detects backend (DATA_PATH env or ./data)
    db := simple.MustConnect()
    defer db.Close()

    users := simple.NewCollection[User](db)

    // Create - ID auto-generated, indexes auto-updated
    user, _ := users.Create(ctx, &User{
        Email: "alice@example.com",
        Name:  "Alice",
    })

    // Query by indexed field
    found, _ := users.FindOne(ctx, "email", "alice@example.com")
}
3

Indexing (Core API)

// ✨ NEW in v1.11: Auto-Indexing with Struct Tags
// Define your indexes on the model, auto-register them. That's it.

type User struct {
    ID    string `json:"id" sb:"id"`
    Email string `json:"email" sb:"index"`
    Role  string `json:"role" sb:"index"`
}

backend := smarterbase.NewFilesystemBackend("./data")
store := smarterbase.NewStore(backend)
redisIndexer := smarterbase.NewRedisIndexer(redisClient)

// Auto-register all indexes from struct tags
smarterbase.AutoRegisterIndexes(redisIndexer, "users", &User{})

// That's it! Indexes are ready to use
indexManager := smarterbase.NewIndexManager(store, redisIndexer)

user := &User{ID: smarterbase.NewID(), Email: "alice@example.com"}
indexManager.Create(ctx, "users/"+user.ID, user)
4

Cascade Deletes (Core API)

// ✨ NEW in v1.11: Declarative Cascade Deletes
// Register parent-child relationships, delete recursively with one call

cascadeIM := smarterbase.NewCascadeIndexManager(store, redisIndexer)

// Register cascade chain: property → areas → photos
cascadeIM.RegisterCascadeChain("properties", []smarterbase.CascadeSpec{
    {
        ChildEntityType: "areas",
        ForeignKeyField: "property_id",
        DeleteFunc:      s.DeleteArea,  // Recursive!
    },
})

cascadeIM.RegisterCascadeChain("areas", []smarterbase.CascadeSpec{
    {
        ChildEntityType: "photos",
        ForeignKeyField: "area_id",
        DeleteFunc:      s.DeletePhoto,
    },
})

// Delete property - automatically cascades to areas & photos!
cascadeIM.DeleteWithCascade(ctx, "properties", propertyKey, propertyID)

Documentation

Comprehensive guides and technical documentation