apadana

module
v0.0.1-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 18, 2026 License: MIT

README

apadana

apadana is a Go SDK that provides building blocks for multi-tenant applications. It handles tenant identification, context propagation, per-tenant configuration, metrics, logging, and instrumentation across common infrastructure: databases, Redis, Kafka, RabbitMQ, NATS, and HTTP.

Installation

go get github.com/PapaDanielVi/apadana

Quick Start

package main

import (
	"context"
	"log/slog"
	"net/http"

	"github.com/PapaDanielVi/apadana/pkg/context"
	"github.com/PapaDanielVi/apadana/pkg/middleware"
)

func main() {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tenantID, _ := context.TenantIDFromContext(r.Context())
		slog.Info("request", "tenant", tenantID)
		w.Write([]byte("hello " + tenantID))
	})

	// Extract tenant ID from X-Tenant-ID header
	mw := middleware.TenantMiddleware(middleware.FromHeader("X-Tenant-ID"))(handler)

	http.ListenAndServe(":8080", mw)
}

Packages

Core
pkg/context — Context Management

Utilities for storing and retrieving tenant IDs in context.Context.

ctx := context.WithTenantID(context.Background(), "acme-corp")
tenantID, ok := context.TenantIDFromContext(ctx)
pkg/replacer — Tenant Replacer

Replaces {tenant_id} placeholders in strings.

s := replacer.Replace("db_{tenant_id}_schema", "acme")
// s == "db_acme_schema"
pkg/config — Config Management

Thread-safe per-tenant configuration storage.

config.Set("acme", "db_host", "localhost:5432")
ctx := context.WithTenantID(context.Background(), "acme")
host, _ := config.Get(ctx, "db_host")
pkg/timezone — Timezone Per Tenant

Manage per-tenant timezone settings.

loc, _ := time.LoadLocation("America/New_York")
timezone.Set("acme", loc)

ctx := context.WithTenantID(context.Background(), "acme")
now := timezone.Now(ctx) // time in tenant's timezone
pkg/obj — Object Management SDK

Lazy-initialized, centralized singleton objects per tenant.

obj.Register("cache", func() (any, error) {
	return make(map[string]string), nil
})

ctx := context.WithTenantID(context.Background(), "acme")
cache, _ := obj.Get(ctx, "cache") // same instance for all calls
Identification & Logging
pkg/middleware — HTTP Middlewares

Extract tenant ID from HTTP requests and inject into context.

// From header
m := middleware.TenantMiddleware(middleware.FromHeader("X-Tenant-ID"))(handler)

// From query parameter
m := middleware.TenantMiddleware(middleware.FromQuery("tenant"))(handler)

// From subdomain
m := middleware.TenantMiddleware(middleware.FromSubdomain())(handler)
pkg/logger — Logger Wrapper

Wraps log/slog to automatically include tenant_id in log output.

ctx := context.WithTenantID(context.Background(), "acme")
logger := logger.New(ctx)
logger.Info("processing order") // includes tenant_id=acme
Instrumentation
pkg/otel — OpenTelemetry Tenant Injection

Automatically adds tenant_id as a span attribute.

processor := otel.NewTenantIDProcessor()
// Register with your OTEL SDK setup
pkg/metrics — Prometheus Metrics

Multi-tenant counters and histograms with automatic tenant_id label.

counter := metrics.NewCounter("http_requests_total", "Total requests")
histogram := metrics.NewHistogram("request_duration_seconds", "Request duration", nil)

ctx := context.WithTenantID(context.Background(), "acme")
counter.Inc(ctx)
histogram.Observe(ctx, 0.5)
Data Layer
pkg/db — Tenant Registry

Multi-tenancy support for MySQL/PostgreSQL using three models:

  • ColumnModel: Adds WHERE tenant_id = 'id' to queries
  • DatabaseModel: Separate database per tenant
  • InstanceModel: Separate connection per tenant
reg := db.New(db.ColumnModel, "mysql", "user:pass@/db")
mw := reg.ColumnMiddleware()
query := mw(ctx, "SELECT * FROM orders")
// query == "SELECT * FROM orders WHERE tenant_id = 'acme'"
pkg/redis — Redis v9 Tools

Wraps go-redis/v9 with automatic key prefixing (tenant:{id}:key).

client := redis.NewClient(ctx, &redis.Options{Addr: "localhost:6379"})
client.Set(ctx, "mykey", "myvalue", 0)
val, _ := client.Get(ctx, "mykey").Result()
Messaging
pkg/rabbitmq — RabbitMQ Tools

Publish and consume messages with X-Tenant-ID header.

// Publish
pub := rabbitmq.NewPublisher(ch)
pub.Publish(ctx, "exchange", "routing-key", []byte("msg"))

// Consume
cons := rabbitmq.NewConsumer(ch)
cons.Consume(ctx, "queue", func(ctx context.Context, d amqp.Delivery) {
	// ctx has tenant ID from message headers
})
pkg/kafka — Kafka Tools

Produce and consume Kafka messages with tenant ID in headers.

// Produce
prod := kafka.NewProducer(writer)
prod.Produce(ctx, "topic", []byte("key"), []byte("value"))

// Consume
cons := kafka.NewConsumer(reader)
cons.Consume(ctx, func(ctx context.Context, msg kafka.Message) {
	// ctx has tenant ID from message headers
})
pkg/nats — NATS Tools

Publish and subscribe with tenant ID in NATS headers.

// Publish
pub := nats.NewPublisher(nc)
pub.Publish(ctx, "subject", []byte("msg"))

// Subscribe
sub := nats.NewSubscriber(nc)
sub.Subscribe(ctx, "subject", func(ctx context.Context, msg *nats.Msg) {
	// ctx has tenant ID from message headers
})
HTTP & Advanced
pkg/httpclient — HTTP Client Tools

Wraps http.Client to inject X-Tenant-ID header into outgoing requests.

client := httpclient.New()
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com", nil)
resp, err := client.Do(ctx, req) // X-Tenant-ID header auto-injected
pkg/burst — Burst Controller Per Tenant

Token bucket rate limiter with separate buckets per tenant.

ctrl := burst.New(10, 5) // 10 req/sec, burst of 5

ctx := context.WithTenantID(context.Background(), "acme")
if ctrl.Allow(ctx) {
	// handle request
}

License

MIT

Directories

Path Synopsis
pkg
burst
Package burst provides per-tenant burst rate limiting.
Package burst provides per-tenant burst rate limiting.
config
Package config provides thread-safe per-tenant configuration storage.
Package config provides thread-safe per-tenant configuration storage.
context
Package tctx provides context utilities for multi-tenant applications.
Package tctx provides context utilities for multi-tenant applications.
db
Package db provides multi-tenant database registry.
Package db provides multi-tenant database registry.
httpclient
Package httpclient provides an HTTP client that injects tenant ID headers.
Package httpclient provides an HTTP client that injects tenant ID headers.
kafka
Package kafka provides multi-tenant Kafka tools.
Package kafka provides multi-tenant Kafka tools.
logger
Package logger provides a slog wrapper that includes tenant ID in log output.
Package logger provides a slog wrapper that includes tenant ID in log output.
metrics
Package metrics provides multi-tenant Prometheus instrumentation.
Package metrics provides multi-tenant Prometheus instrumentation.
middleware
Package middleware provides HTTP middleware for tenant identification.
Package middleware provides HTTP middleware for tenant identification.
nats
Package nats provides multi-tenant NATS tools.
Package nats provides multi-tenant NATS tools.
obj
Package obj provides lazy-initialized, centralized per-tenant object management.
Package obj provides lazy-initialized, centralized per-tenant object management.
otel
Package otel provides OpenTelemetry instrumentation for multi-tenant apps.
Package otel provides OpenTelemetry instrumentation for multi-tenant apps.
rabbitmq
Package rabbitmq provides multi-tenant RabbitMQ tools.
Package rabbitmq provides multi-tenant RabbitMQ tools.
redis
Package redis provides multi-tenant Redis v9 tools.
Package redis provides multi-tenant Redis v9 tools.
replacer
Package replacer provides tenant ID replacement utilities.
Package replacer provides tenant ID replacement utilities.
timezone
Package timezone provides per-tenant timezone management.
Package timezone provides per-tenant timezone management.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL