Turn Redis (indexes) + S3 (storage) into a queryable, transactional document store. 85% cost savings. Zero database complexity.
// 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")
99.999999999% data durability via S3 multi-AZ replication
Automatic index repair and circuit breaker protection built-in
S3 handles durability automatically, no backup strategies needed
JSON documents with built-in versioning, migrations happen on read
Append-only event logs, perfect for audit trails and activity logs
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",
)
Indexes stored in Redis Sets. Query by any field with constant-time lookups. Redis is required for indexing.
Redis distributed locks eliminate S3 race conditions. True atomic operations across multiple servers.
// 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
Prometheus metrics and structured logging out of the box. Know exactly what's happening in production.
Add a version field, register migrations, and your data transforms automatically on read. No ALTER TABLE statements, no downtime.
// 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)
If you don't need JOINs or complex aggregations, you probably don't need a database
Store user profiles, preferences, sessions. No schema migrations when adding new fields.
Orders, invoices, product catalogs. Index by user, status, date without complex queries.
App configs, feature flags, tenant settings. Update instantly without migrations.
Blog posts, articles, documentation. Store rich JSON without rigid schemas.
Audit trails, activity logs. Append-only JSONL format for efficient writes.
Per-tenant configuration and data. Scale without database partitioning complexity.
85% cheaper than managed databases (1TB data, 1M requests/day)
$41 /month
$271 /month
$300 /month
$100 /month
Get started in minutes with Go 1.18+
go get github.com/adrianmcphee/smarterbase/v2
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")
}
// ✨ 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)
// ✨ 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)
Comprehensive guides and technical documentation
Complete user guide with examples, quick start, and best practices.
Architecture, performance characteristics, and detailed specifications.
Development setup, testing guidelines, and contribution workflow.
7 working examples with interactive code and comparisons. User management, e-commerce, event logging, and more.