CLI: sdk-api — Module: github.com/natuleadan/sdk-api
A production-ready Go SDK for building event-driven microservices and monoliths. Fork of go-zero with Fiber (fasthttp), pgx (PostgreSQL), NATS JetStream + Kafka (messaging), per-route middleware, security, and YAML-driven configuration.
go install github.com/natuleadan/sdk-api/cmd/sdk-api@latestOr download a pre-built binary from the releases page.
Create a new service with a single command:
sdk-api new products-svc --model Product --fields "name:string,price:float64"
cd products-svcThis generates service.yaml, main.go, models/product.go, and models/hooks.go:
name: products-svc
port: 8080
databases:
- name: pg-main
driver: postgres
url: "${DATABASE_URL}"
entry:
- type: crud
model: Product
db: pg-main
table: products
server:
openapi:
enabled: trueStart PostgreSQL and run:
docker run -d --name pg -p 5432:5432 \
-e POSTGRES_USER=dev -e POSTGRES_PASSWORD=devpass \
postgres:18-alpine
DATABASE_URL="postgres://dev:devpass@localhost:5432/postgres?sslmode=disable" \
go run .Your service is live with auto-generated CRUD, OpenAPI docs, and Prometheus metrics:
curl http://localhost:8080/api/v1/products
open http://localhost:8080/docs
curl http://localhost:8080/health| Category | Feature | Description |
|---|---|---|
| Entry types | crud |
Auto-generated List/Get/Create/Update/Delete with hooks |
rest |
Custom handler, any method (GET/POST/PUT/PATCH/DELETE) | |
webhook |
Defaults to POST, no JWT by default | |
websocket |
WebSocket upgrade handler | |
sse |
Server-Sent Events streaming | |
file |
Upload/download/delete, S3 or local storage | |
async |
202 Accepted + status polling for long-running jobs | |
graphql |
Auto-generated schema + CRUD resolvers | |
| Event streams | NATS JetStream | Push/pull consumers, KV, request-reply |
| Kafka | Topic-based pub/sub, consumer groups | |
EventBroker |
Unified interface for NATS/Kafka duality | |
| Security | Security headers | X-Frame-Options, CSP, HSTS, Permissions-Policy, etc. |
| CSRF | Double-submit cookie pattern | |
| Rate limiting | Token bucket (memory or Redis), per IP/user/global | |
| TLS | Manual certs or Let's Encrypt autocert | |
| SSRF protection | Blocks private/loopback/metadata IPs | |
| Input validation | Struct tag validation via go-playground/validator/v10 |
|
| Error sanitization | Generic 500 messages, no internal leak | |
| Auth | JWT per-entry | Algorithm pinning, claim validation, AuthContext extraction |
| 4 auth modes | none, manual, openfga-zitadel, ory |
|
| OpenFGA + Zitadel | ReBAC authorization + OIDC authentication | |
| Ory Kratos + Keto | Identity management + fine-grained authorization | |
| API keys | Service-to-service with scoped permissions | |
| Token refresh | Delegated to IDP (Zitadel/Kratos) or manual re-sign | |
| Cron | nats mode |
Publish message to NATS on schedule |
handler mode |
Call a Go function on schedule | |
internal mode |
System tick without handler | |
| Databases | PostgreSQL | pgxpool with auto-sizing pool |
| MySQL | via go-sql-driver/mysql |
|
| Turso | Embedded SQLite via libsql | |
| Multi-DB | Each entry/exit can use a different database | |
| Server | 14 built-in middlewares | Logger, breaker, JWT, CORS, tracing, shedding, timeout, etc. |
| Per-route middleware | Configure middlewares per path in YAML | |
| Security headers + CSRF + Rate limit | YAML-driven security layers | |
| TLS (manual + autocert) | HTTPS + HTTP→HTTPS redirect | |
| Error sanitization + CRLF protection | Built-in, always on | |
| OpenAPI 3.0.3 + Scalar | Auto-generated spec + UI /docs |
|
| Health + Metrics | /health, /metrics |
|
| Storage | S3-compatible | MinIO, R2, AWS S3 |
| Local filesystem | Path-constrained uploads |
| Scenario | Stack | RPS |
|---|---|---|
| Minimal HTTP | raw Fiber | ~700k |
| URL expand | SDK (PG + Redis) | ~31k |
| URL expand | SDK (PG + NATS KV) | ~31k |
| Product by ID | SDK (MySQL) | ~25k |
| Product by ID | SDK (Turso) | ~20k |
| Product by ID | SDK (MongoDB) | ~18k |
Full benchmarks in docs/benchmarks.md.
import "github.com/natuleadan/sdk-api"
// Option 1: YAML-driven
svc, _ := runtime.New("service.yaml")
pool := svc.Pool("pg-main").(*pgxpool.Pool)
table, _ := db.NewTable[Product](pool, "products")
svc.WithCRUD("Product", runtime.NewCRUDProvider(table, &ProductHooks{}))
svc.WithExit("onOrderConfirmed", func(ctx, msg []byte) ([]byte, error) {
log.Printf("received: %s", string(msg))
return nil, nil
})
svc.RegisterModel("Product", (*Product)(nil))
svc.Run()
// Option 2: Client mode (connect to existing service)
client, _ := runtime.NewClient("service.yaml")
resp, _ := client.Request("orders", "orders.transform", []byte(`{"id": 1}`))| Field | Default | Description |
|---|---|---|
host |
0.0.0.0 |
Bind address |
prefork |
false |
Fiber prefork (SO_REUSEPORT, Linux only) |
body_limit |
4194304 |
Max body size |
timeout |
30s |
Read/write/idle timeout |
api_prefix |
/api/v1 |
Prefix for all entry paths |
health_path |
/health |
Liveness probe |
metrics_path |
/metrics |
Prometheus endpoint |
shutdown_timeout |
10s |
Graceful shutdown wait |
| Type | HTTP Methods | What you write | DB required |
|---|---|---|---|
crud |
GET, POST, PATCH, DELETE | Nothing | Yes |
rest |
Any | Single handler | Optional |
webhook |
Any (default POST) | Single handler | Optional |
websocket |
GET (upgrade) | WS handler | No |
sse |
GET | SSE handler | No |
file |
GET, POST, PUT, PATCH, DELETE | Upload handler | No |
async |
POST, GET | Async handler | No |
graphql |
POST | Nothing | No |
| Field | Default | Description |
|---|---|---|
subscribe.stream |
required | NATS stream name |
subscribe.subject |
stream name | Subject to subscribe to |
handler |
required | Go function name |
max_concurrent |
1 | Max concurrent messages |
reply |
false | Enable request-reply via msg.Respond() |
reply_timeout |
30s |
Reply timeout |
consumer_mode |
push |
push or pull |
pull_batch |
0 | Batch size for pull consumers |
HTTP Entry → nats_publish → NATS Stream → Exit Worker → DB/NATS/Email
↑ │
└────── Request-Reply (reply: true) ───┘
Cron → Publish NATS → Exit Worker (subscribed)
Packages involved:
┌──────────┬─────────────┬──────────────┬─────────────┬───────────┐
│ db/ │ server/ │ events/ │ runtime/ │ auth/ │
│ (pgx) │ (Fiber) │ (NATS JS) │ (orchestr.) │ (ext.) │
│ │ │ │ │ │
│ Table[T] │ 14+ middle- │ Producers │ Service │ OpenFGA │
│ CRUD │ wares │ Exit Workers│ YAML cfg │ Zitadel │
│ AutoInit │ JWT/CORS │ KV Cache │ Entry routes│ Ory │
│ PG/Turso │ SSE/WS │ Request- │ Exit workers│ API Keys │
│ MySQL │ OpenAPI │ Reply │ Cron │ AuthCtx │
│ │ Auth (4 modes)│ │ │ │
└──────────┴─────────────┴──────────────┴─────────────┴───────────┘
| File | Contents |
|---|---|
| docs/configuration.md | Full YAML schema reference (all entry types, event streams, security) |
| docs/security.md | Consolidated security guide (headers, CSRF, rate limit, TLS, SSRF, validation, auth, JWT, OpenFGA, Zitadel, Ory, API keys) |
| docs/http-server.md | Server config, middlewares, TLS, security headers, CSRF, rate limit |
| docs/runtime.md | Runtime API: Service, CRUDProvider, RegisterValidation, SafeHTTPClient |
| docs/messaging.md | NATS + Kafka, EventBroker, producers, exit workers, KV cache |
| docs/database.md | PG/Turso/MySQL, pool sizing, multi-DB, Table[T] CRUD |
| docs/entry-graphql.md | Auto-generated GraphQL schema + resolvers |
| docs/entry-async.md | Async jobs: 202 Accepted + status polling |
| docs/secrets.md | Secrets management: ${VAR}, SOPS/age, best practices |
| docs/api-patterns.md | Entry types, exit workers, cron, hooks with YAML + Go examples |
| docs/best-practices.md | Gotchas, patterns, anti-patterns |
| docs/cli.md | sdk-api new/docker/kube/client subcommands |
| docs/benchmarks.md | Full benchmark results and methodology |
| docs/architecture.md | Architecture, entry router, exit system, cron design |
| docs/getting-started.md | Step-by-step tutorial for first service |
entry:
- type: crud
model: Product
db: pg-main
table: products
path: /productstype ProductHooks struct{ runtime.DefaultHooks[Product] }
func (h *ProductHooks) BeforeCreate(ctx context.Context, req Product) (Product, error) {
req.CreatedAt = time.Now()
return req, nil
}
pool := svc.Pool("pg-main").(*pgxpool.Pool)
table, _ := db.NewTable[Product](pool, "products")
svc.WithCRUD("Product", runtime.NewCRUDProvider(table, &ProductHooks{}))entry:
- type: rest
method: POST
path: /orders/transform
handler: onTransform
nats_publish:
- stream: orders
subject: orders.transformedsvc.WithRest("onTransform", func(c *fiber.Ctx) error {
var req Order
jsonx.Unmarshal(c.Body(), &req)
req.Status = "transformed"
return c.JSON(req)
})exit:
- name: email-sender
subscribe:
stream: orders
subject: orders.confirmed
handler: onEmail
reply: true
reply_timeout: 30ssvc.WithExit("onEmail", func(ctx, msg []byte) ([]byte, error) {
log.Printf("sending email for: %s", string(msg))
return []byte(`{"sent": true}`), nil
})cron:
- name: daily-report
schedule: "0 6 * * *"
mode: handler
handler: onDailyReportsvc.WithCron("onDailyReport", func(ctx context.Context) error {
log.Println("generating daily report...")
return nil
})cmd/sdk-api/ # CLI generator (new/docker/kube/client)
runtime/ # Service orchestrator, entry router, exit workers, cron, hooks
server/ # Fiber HTTP + 14 middlewares + storage backends
db/ # Table[T] CRUD (PG, Turso, MySQL) + AutoInit
events/ # NATS JetStream producers, consumers, KV cache, request-reply
infra/ # 45+ go-zero packages (conf, logx, trace, breaker, redis, discover, ...)
docs/ # Documentation + API patterns
examples/ # 8 dockerized example services + benchmarks