Go Framework
The Putnami Go framework follows the same Putnami architecture in idiomatic Go. It provides the same core abstractions — dependency injection, HTTP server, security, events, storage, and service clients — using Go's strengths: goroutines, channels, generics, context.Context, and interfaces.
Installation
All Go packages use the go.putnami.dev vanity import domain. The go.putnami.dev host serves go-import meta tags that redirect go get to the source repository.
go get go.putnami.dev/app
go get go.putnami.dev/http
go get go.putnami.dev/injectPackage overview
| Package | Description |
|---|---|
inject |
Hierarchical DI container with named tokens, generics, scoped providers, cycle detection |
config |
Multi-source configuration (YAML, env vars, maps) with struct tags and DI bridge |
logger |
Structured logging via slog with pluggable sinks (JSON default, console, buffer, memory) |
schema |
Struct-tag-based validation with type coercion |
errors |
Structured error model with typed codes, categories, stack capture, HTTP mapping |
app |
Plugin-based application lifecycle with module composition and optional fx-style DI |
http |
HTTP server, trie-based router, middleware (recovery, logging, rate limiting, compression) |
security |
Declarative authorization middleware (roles, scopes, custom guards) |
grpc |
gRPC server with Connect protocol gateway and DI-scoped requests |
openapi |
OpenAPI 3.0.3 spec generation from Go struct types |
sql |
PostgreSQL via pgx — connection pool, repository pattern, migrations, query builder |
cache |
Layered caching (memory + disk) with TTL and FIFO eviction |
telemetry |
OpenTelemetry integration for distributed tracing and metrics |
events |
Typed event system with topics, handlers, retry, and dead-letter queues |
storage |
Object storage with pluggable backends (memory, filesystem, S3) |
client |
Service client builder with retry, circuit breaker, and interceptor chain |
Quick start
Explicit wiring (idiomatic Go)
package main
import (
"context"
"go.putnami.dev/app"
"go.putnami.dev/config"
fhttp "go.putnami.dev/http"
)
type AppConfig struct {
Port int `json:"port" default:"8080" env:"PORT"`
DSN string `json:"dsn" env:"DATABASE_URL"`
}
func main() {
cfg, _ := config.Load(config.Config[AppConfig]("app"))
db := connectDB(cfg.DSN)
users := NewUserService(db)
server := fhttp.NewServerPlugin(fhttp.ServerConfig{Port: cfg.Port})
server.Use(fhttp.Recovery())
server.GET("/users", listUsersHandler(users))
a := app.New("my-service")
a.Module.Use(server)
a.Run(func(ctx context.Context) error {
<-ctx.Done()
return nil
})
a.ListenAndServe()
}Constructor-based DI (fx-style)
func main() {
a := app.New("my-service")
a.ProvideFunc(
LoadConfig,
connectDB,
NewUserService,
newHTTPServer,
)
a.InvokeFunc(func(server *fhttp.ServerPlugin, users *UserService) {
server.GET("/users", listUsersHandler(users))
})
a.ListenAndServe()
}Both approaches can be mixed freely. DI is entirely optional — applications that register no providers skip container creation entirely.
Architecture
The framework is organized into four layers:
Foundation: errors, logger, config, schema
DI: inject (optional, standalone)
Application: app → http, grpc, sql, events, storage, cache, client
Cross-cutting: security, telemetry, openapiEvery component is a plugin that participates in the application lifecycle. Plugins compose into modules, and modules form a tree rooted at the application. See Plugins & Lifecycle for details.
Design principles
- Stdlib only — no external runtime dependencies (except pgx for PostgreSQL and gRPC)
- Optional DI — wire services explicitly or use constructor-based DI; both compose naturally
- Generics — type-safe topics, repositories, caches, and tokens via Go 1.18+ generics
- Context-driven — transactions, scopes, loggers, and traces all flow through
context.Context - Plugin architecture — every framework component implements the same lifecycle interface
- Goroutine-safe — shared state uses
sync.Mutex, channels, or atomics
TypeScript ↔ Go comparison
| Concept | TypeScript | Go |
|---|---|---|
| DI tokens | named<T>('key') |
inject.Named[T]("key") |
| DI scope | AsyncLocalStorage |
context.Context |
| Event topics | topic('name', schema) |
events.NewTopic[T]("name") |
| Event handlers | handler(topic).handle(fn) |
events.Handle(topic, fn, opts...) |
| Storage buckets | Bucket('name', opts) |
storage.Bucket("name", opts...) |
| Client builder | ClientBuilder.for(Client) |
client.NewBuilder() |
| Circuit breaker | Custom implementation | client.CircuitBreaker |
| Config | Config('name', schema) |
config.Config[T]("name") |
| Validation | Schema DSL | Struct tags |
| Application | application().use(plugin) |
app.New("name").Module.Use(plugin) |