⚓ Anclax
English | 中文
Build serverless, reliable apps at lightspeed ⚡ — with confidence 🛡️.
Anclax is a definition‑first framework for small–medium apps (single PostgreSQL). Define APIs and tasks as schemas; generated code moves correctness to compile time.
Join our Discord server.
Highlights ✨
- YAML-first, codegen-backed: Define HTTP and task schemas in YAML; Anclax generates strongly-typed interfaces so missing implementations fail at compile time, not in prod.
- Async tasks you can trust: At-least-once delivery, automatic retries, and cron scheduling out of the box.
- Transaction-safe flows: A
WithTx
pattern ensures hooks always run and side effects are cons…
⚓ Anclax
English | 中文
Build serverless, reliable apps at lightspeed ⚡ — with confidence 🛡️.
Anclax is a definition‑first framework for small–medium apps (single PostgreSQL). Define APIs and tasks as schemas; generated code moves correctness to compile time.
Join our Discord server.
Highlights ✨
- YAML-first, codegen-backed: Define HTTP and task schemas in YAML; Anclax generates strongly-typed interfaces so missing implementations fail at compile time, not in prod.
- Async tasks you can trust: At-least-once delivery, automatic retries, and cron scheduling out of the box.
- Transaction-safe flows: A
WithTx
pattern ensures hooks always run and side effects are consistent. - Typed database layer: Powered by
sqlc
for safe, fast queries. - Fast HTTP server: Built on Fiber for performance and ergonomics.
- AuthN/Z built-in: Macaroons-based authentication and authorization.
- Pluggable architecture: First-class plugin system for clean modularity.
- Ergonomic DI: Wire-based dependency injection keeps code testable and explicit.
Why Anclax? (The problem it solves) 🤔
- Glue-code fatigue: Many teams stitch HTTP, DB, tasks, DI, and auth by hand, leaving implicit contracts and runtime surprises. Anclax makes those contracts explicit and generated.
- Background jobs are hard: Idempotency, retries, and delivery guarantees are non-trivial. Anclax ships a task engine with at-least-once semantics and cron.
- Consistency across boundaries: Keep handlers, tasks, and hooks transactional using
WithTx
so invariants hold. - Confidence and testability: Every generated interface is mockable; behavior is easy to test.
Key advantages 🏆
- Compile-time confidence: Schema → interfaces → concrete implementations you cannot forget to write.
- Productivity:
anclax init
+anclax gen
reduces boilerplate and wiring. - Extensibility: Clean plugin boundaries and event-driven architecture.
- Predictability: Singletons for core services, DI for clarity, and well-defined lifecycles.
Architecture 🏗️
Anclax helps you build quickly while staying scalable and production‑ready.
- Single PostgreSQL backbone: One PostgreSQL database powers both transactional business logic and the durable task queue, keeping state consistent and operations simple. For many products, a well‑provisioned instance (e.g., 32 vCPU) goes a very long way.
- Stateless application nodes: HTTP servers are stateless and horizontally scalable; you can run multiple replicas without coordination concerns.
- Task queue as integration fabric: Use async tasks to decouple modules. For example, when a payment completes, enqueue an
OrderFinished
task and do any factory‑module inserts in its handler—no factory logic inside the payment module. - Built‑in worker, flexible deployment: Anclax includes an async task worker. Run it in‑process, as separate long‑running workers, or disable it for serverless HTTP (e.g., AWS Lambda) while keeping workers on regular servers.
- Monolith, not microservices: Anclax favors a pragmatic, scalable monolith and is not aimed at multi‑million QPS microservice fleets. These choices maximize early velocity and give you a clear, reliable path to scale with confidence.
Hands-on: try it now 🧑💻
# 1) Scaffold into folder 'demo'
anclax init demo github.com/you/demo
# 2) Generate code (can be re-run anytime)
cd demo
anclax gen
# 3) Start the stack (DB + API + worker)
docker compose up
In another terminal:
curl http://localhost:2910/api/v1/counter
# Optional sign-in if your template includes auth
curl -X POST http://localhost:2910/api/v1/auth/sign-in -H "Content-Type: application/json" -d '{"name":"test","password":"test"}'
One‑minute tour 🧭
- Define an endpoint (OpenAPI YAML) 🧩
paths:
/api/v1/counter:
get:
operationId: getCounter
- Define a task ⏱️
tasks:
incrementCounter:
description: Increment the counter value
cron: "*/1 * * * *"
- Generate and implement 🛠️
anclax gen
func (h *Handler) GetCounter(c *fiber.Ctx) error {
return c.JSON(apigen.Counter{Count: 0})
}
Showcase: unique features 🧰
OpenAPI-powered middleware (no DSL)
x-check-rules:
OperationPermit:
useContext: true
parameters:
- name: operationID
schema:
type: string
ValidateOrgAccess:
useContext: true
parameters:
- name: orgID
schema:
type: integer
format: int32
paths:
/orgs/{orgID}/projects/{projectID}:
get:
operationId: GetProject
security:
- BearerAuth:
- x.ValidateOrgAccess(c, orgID, "viewer")
- x.OperationPermit(c, operationID)
Security scheme (JWT example)
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: macaroon
Async tasks: at-least-once, retries, cron
# api/tasks.yaml
tasks:
- name: SendWelcomeEmail
description: Send welcome email to new users
parameters:
type: object
required: [userId, templateId]
properties:
userId:
type: integer
format: int32
templateId:
type: string
retryPolicy:
interval: 5m
maxAttempts: 3
cron: "0 * * * *"
// Enqueue outside a tx
taskID, _ := taskrunner.RunSendWelcomeEmail(ctx, &taskgen.SendWelcomeEmailParameters{
UserId: 123, TemplateId: "welcome",
}, taskcore.WithUniqueTag("welcome-email:123"))
// Enqueue atomically with your business logic
_ = model.RunTransactionWithTx(ctx, func(tx pgx.Tx, txm model.ModelInterface) error {
// ... create user ...
_, err := taskrunner.RunSendWelcomeEmailWithTx(ctx, tx, &taskgen.SendWelcomeEmailParameters{
UserId: user.ID, TemplateId: "welcome",
})
return err
})
Transactions: compose everything with WithTx
func (s *Service) CreateUserWithTx(ctx context.Context, tx pgx.Tx, username, password string) (int32, error) {
txm := s.model.SpawnWithTx(tx)
userID, err := txm.CreateUser(ctx, username, password)
if err != nil { return 0, err }
if err := s.hooks.OnUserCreated(ctx, tx, userID); err != nil { return 0, err }
_, err = s.taskRunner.RunSendWelcomeEmailWithTx(ctx, tx, &taskgen.SendWelcomeEmailParameters{ UserId: userID })
return userID, err
}
Dependency injection with Wire
func NewGreeter(m model.ModelInterface) (*Greeter, error) { return &Greeter{Model: m}, nil }
func InitApp() (*app.App, error) {
wire.Build(model.NewModel, NewGreeter /* ...other providers... */)
return nil, nil
}
Typed SQL with sqlc
-- name: GetCounter :one
SELECT value FROM counter LIMIT 1;
-- name: IncrementCounter :exec
UPDATE counter SET value = value + 1;
Running async tasks ⚙️
// Trigger the incrementCounter task
taskID, err := taskrunner.RunIncrementCounter(ctx, &taskgen.IncrementCounterParameters{})
if err != nil {
// handle error
}
Tasks run with at-least-once delivery guarantees and automatic retries based on your retry policy. You can also schedule tasks via cron expressions in api/tasks.yaml
.
Advanced: Custom initialization 🧩
You can run custom logic before the app starts by providing an Init
function:
// Runs before the application starts
func Init(anclaxApp *anclax_app.Application, taskrunner taskgen.TaskRunner, myapp anclax_app.Plugin) (*app.App, error) {
if err := anclaxApp.Plug(myapp); err != nil {
return nil, err
}
if _, err := anclaxApp.GetService().CreateNewUser(context.Background(), "test", "test"); err != nil {
return nil, err
}
if _, err := taskrunner.RunAutoIncrementCounter(context.Background(), &taskgen.AutoIncrementCounterParameters{
Amount: 1,
}, taskcore.WithUniqueTag("auto-increment-counter")); err != nil {
return nil, err
}
return &app.App{ AnclaxApp: anclaxApp }, nil
}
To customize how the Anclax application is constructed, override InitAnclaxApplication
:
func InitAnclaxApplication(cfg *config.Config) (*anclax_app.Application, error) {
anclaxApp, err := anclax_wire.InitializeApplication(&cfg.Anclax, anclax_config.DefaultLibConfig())
if err != nil {
return nil, err
}
return anclaxApp, nil
}
Need more dependencies inside Init
? Add them as parameters (e.g., model.ModelInterface
) and run anclax gen
.
Documentation 📚
- Transaction Management: docs/transaction.md (中文)
- Middleware (x-functions & x-check-rules): docs/middleware.md (中文)
- Async Tasks: Tutorial docs/async-tasks-tutorial.md · Tech reference docs/async-tasks-technical.md (中文, 中文)
Examples 🧪
examples/simple
— minimal end-to-end sample with HTTP, tasks, DI, and DB.