Skip to content

Repository files navigation


Documentation DeepWiki Coverage Build Status
zerofmk.in Ask DeepWiki Coverage Build Status


Zero Framework

mascot

One binary. No GC. Build config-driven microservices in Zig.

Zero is a batteries-included web framework for Zig that wires REST, SQL, NoSQL, cache, pub/sub, auth, GraphQL, Protobuf, search, metrics and tracing into a single static binary and configured almost entirely through .env.

  • Zero boilerplate — databases, queues, auth and observability plug in with no glue code.
  • One static binary — ~16–65 MiB RSS, no runtime, ships anywhere (including Kubernetes).
  • Observable by default — structured JSON logs, Prometheus metrics, distributed tracing and health endpoints from the first request.
  • Fast and small — tens of thousands of requests/sec at ~50 MiB RSS, no GC pauses, no JIT warm-up.

Quick Start

Drop this in src/main.zig, add a configs/.env, and you have a JSON API with structured logs and /metrics live:

const std = @import("std");
const zero = @import("zero");
const utils = zero.utils;

pub const std_options: std.Options = .{ .logFn = zero.logger.custom };

pub fn main(init: std.process.Init) !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const app = try zero.App.new(arena.allocator(), init.io, init.environ_map);
    try app.get("/json", jsonResponse);
    try app.run();
}

fn jsonResponse(ctx: *zero.Context) !void {
    try ctx.json(.{ .msg = "hello zero!" });
}
zig fetch --save https://github.com/im-ng/zero/archive/refs/heads/main.zip
mkdir -p configs && printf 'APP_NAME=hello\nHTTP_PORT=8080\n' > configs/.env
zig build run
curl localhost:8080/json   # => {"msg":"hello zero!"}

That's the whole app. Everything else - Postgres, Redis, Kafka, auth, metrics, is opt-in through configuration.

Full walkthrough in Hello Zero and Getting Started.

Why Zero?

If you want Go's ergonomics without its runtime, or Node's speed without its footprint, Zero gives you a strongly-opinionated Zig framework: explicit memory, a single binary, and the microservice building blocks you'd otherwise wire together by hand.

Start with Getting Started or jump straight to the Examples.

Features

Category Status Details
REST / CRUD Standard REST endpoints out of the box; AutoCRUD for struct models
Configuration .env with per-environment overrides (/configuration)
Logging Structured, UTC/local timestamps (/logging)
Metrics App, HTTP, SQL, KV + process/memory stats (/observability)
Tracing TraceID middleware, request-level tracing (/x-ray)
Auth Basic, API Key, OAuth 2.0 (/authentication)
CORS / Panic Recovery Configurable CORS, automatic panic recovery
Databases PostgreSQL, SQLite, Redis, DuckDB, InfluxDB, Solr, Cassandra (/sqlite, /duckdb, /cassandra, /influxdb, /solr)
Pub/Sub MQTT, NATS, Kafka (librdkafka), Redis (/pubsub)
Migrations DB migrations + seed on startup (/migrations)
HTTP Client Register multiple external services with auth + circuit breakers (/http-service)
Cron Jobs * * * * * + second-level + range support (/cronz)
WebSockets Built-in WebSocket support (/websocket)
Static Files Serve static assets + Swagger UI (/swagger)
Health Checks Liveness + status endpoints
GraphQL Schema-less resolvers over HTTP (/graphql)
Protobuf proto3 codegen + bind/decode & encode over HTTP (/protobuf)

See feature parity for the full roadmap.

What's New

Recent additions (full detail on zerofmk.in):

  • Zig 0.16 + std.Io injectionApp.new(allocator, io, em) threads the process I/O reactor through container/Context; tests are consolidated at each file's end. See Migrating to 0.16.

  • DuckDB in-process OLAP — register an embedded SQL engine with app.addDuckDB(":memory:"), no external service. See DuckDB.

  • AutoCRUD for DuckDBapp.addRestHandlers now targets DuckDB too. See Auto CRUD.

  • CLI Application Mode — build one-shot jobs or long-running migrations with App.newCmd + app.SubCommand, no HTTP server. See CLI Mode.

  • Kubernetes deployment — sample image build + manifests. See Kubernetes and Container.

  • New data sources — Cassandra, InfluxDB and Solr join the supported backends. See /cassandra, /influxdb, /solr.

  • Bootstrap arena — Pre-allocated memory for framework bootstrap (bounded RSS). See Architecture.

  • Outbound rate limiting & REST handlers — per-service rate limits and struct-model REST handlers for external services. See Rate Limiter and REST Handler.

  • Resilience — circuit breakers, request timeouts/bulkheads, and pub/sub reconnect + dead-letter. See Resilience.

  • Observability — distributed tracing and structured metrics/tracing wired in from the first request. See Observability.

  • Benchmarks in CI — reproducible throughput/latency/RSS runs. See Benchmark.

Examples

Every feature ships with a runnable example under examples/. Pick one and zig build run from its directory.

Basics

  • zero-basic — JSON / text routes, DB read, key-value, local file store.
  • zero-stream — live host/cpu/status metrics streamed over WebSocket + cron.
  • zero-websocket — minimal WebSocket handler.
  • zero-todo-htmx — HTMX UI backed by AutoCRUD.
  • zero-cli — CLI mode: one-shot jobs / migrations, no HTTP server.
  • zero-cronz — scheduled cron jobs (* * * * * *).

Data & Stores

Messaging

Auth & API

GraphQL & Protobuf

Migrations & Deployment

Documentation

The full, versioned documentation lives at zerofmk.in (Zig 0.16 is the live locale; 0.15.2 is frozen under /0.15.2/). You can also ask the repo anything via DeepWiki. Per-feature deep dives:

Configuration

Zero is configured through configs/.env with per-environment overrides (configs/.dev.env, etc.). A minimal file:

APP_NAME=my-app
HTTP_PORT=8080
# PostgreSQL
DB_DIALECT=postgres
DB_HOST=localhost
DB_USER=user1
DB_PASSWORD=password1
DB_NAME=mydb
DB_PORT=5432
# Auth
AUTH_MODE=Basic

The complete list of keys (Redis, DuckDB, InfluxDB, Solr, Cassandra, Kafka, MQTT, TLS, metrics, logging, rate limiting, RBAC, …) is in Configuration.

Resilience

Resilience is configured, not coded. Request timeouts/bulkheads, datasource circuit breakers, pub/sub auto-reconnect with dead-letter, and structured logging are all on by default or via env. For outbound services you can also set limits explicitly:

var svc_opts: zero.client.ServiceOptions = .{};
svc_opts.circuitBreaker = .{ .failure_threshold = 5, .cooldown_ms = 30_000 };
try app.addHttpService("auth-service", app.config.get("SERVICE_URL"), svc_opts);

See Resilience for timeouts, bulkheads and DLQ behavior.

Metrics & Observability

Prometheus metrics are exposed at /metrics and a health endpoint at /.well-known/health (liveness/status). Distributed tracing via TraceID is attached to every request.

curl localhost:8080/metrics   # http_requests_total, app_sql_response, ...
curl localhost:8080/.well-known/health

See Observability

Data & Stores

Attach a datastore with one call; ctx.SQL, ctx.KV, ctx.FileStore light up automatically.

// In-process OLAP SQL — no external service required.
try app.addDuckDB(":memory:");
// or a relational backend:
// try app.addSQL(...);  try app.addSQLite(...);  try app.addNoSQL(...);

fn listUsers(ctx: *zero.Context) !void {
    const users = try ctx.SQL.queryRows(ctx, User, "SELECT id, name FROM users", .{});
    try ctx.json(.{ .data = users });
}

SQL, NoSQL, cache, pub/sub and file stores:

/sqlite, /duckdb, /cassandra, /influxdb, /solr, /kv-store, /file-store.

Auto CRUD

One line wires list / get / create / update / delete for a struct model:

const User = struct { id: i64, name: []const u8, email: []const u8 };
try app.addRestHandlers(User, .{ .resource = "users" });
// GET/POST/PUT/DELETE /users, /users/:id

See Auto CRUD.

Auth

Inbound auth is config-driven (AUTH_MODE = Basic | APIKey | OAuth). Handlers read the verified claims from the context:

try app.get("/basic", basicResponse);

fn basicResponse(ctx: *zero.Context) !void {
    const claims = try ctx.getUsername();
    try ctx.json(claims.?);
}

See Authentication.

Pub/Sub & Messaging

Subscribe to MQTT, NATS or Kafka; the same handler shape works for all:

try app.addKafkaSubscription("topic", onMessage);
// MQTT: try app.addSubscription("topic", onMessage);
// NATS:  try app.addPubSubSubscription("topic", onMessage);

fn onMessage(ctx: *zero.Context) !void {
    // ctx.message holds the payload
}

See Pub/Sub, Kafka, NATS.

GraphQL

Schema-less resolvers over HTTP (POST/GET). Struct fields map to types; function fields are invoked as resolvers:

try app.addGraphQL(query_root, mutation_root);

See GraphQL.

Protobuf

Generate structs from .proto and bind/decode over HTTP:

const msg = try ctx.bindProto(MyProtoMsg);  // POST body -> struct
try ctx.protobuf(msg);                      // struct -> response bytes

See Protobuf.

CLI Application Mode

Build one-shot commands or long-running migrations without an HTTP server:

pub fn main(init: std.process.Init) !void {
    const app = try zero.App.newCmd(allocator, init.io, init.environ_map);
    try app.SubCommand("migrate", runMigration, .{ .description = "run migrations" });
    try app.runCmd(init.minimal.args);
}

See CLI Mode.

Testing

Handlers are plain functions over *Context, so they're trivial to unit test. The framework's own suite runs 130+ tests with coverage; the harness is documented in Testing:

test "health responds ok" {
    const ctx = try Context.initCli(allocator, container);
    try ctx.json(.{ .ok = true });
}

Benchmark

The bundled benchmark harness drives a concurrency ramp and reports throughput, latency percentiles and per-level RSS. CI runs it for regression. Reproduce in Benchmark.

Container Deployment

Build a single static binary and deploy anywhere. Sample image + manifests in Kubernetes and Container:

FROM alpine:latest
COPY zig-out/bin/app /app
EXPOSE 8080
ENTRYPOINT ["/app"]

Zig Version Compatibility

Branch Version
main 0.16.0 (experimental)
stable-0.15.2 0.15.2

For stable work prefer the stable-0.15.2 branch; main tracks the 0.16 experimental baseline (including the std.Io injection described above). See Migrating to 0.16.

Project Structure

src/                  framework source (App, Context, container, datasource, pubsub, …)
examples/             26 runnable example apps
configs/.env          per-environment configuration
static/               embedded swagger UI + framework assets
build.zig             build wiring (test / integration / validation / bench)

Known Gotchas

  • Kafka requires the system librdkafka dev package to be installed and linked weakly.

    apt install librdkafka-dev brew install librdkafka

  • DuckDB needs libduckdb.so/duckdb.h on the library path (see DuckDB).

  • The kafka build option is commented out; rdkafka is always linked.

  • Auth modes: Basic, APIKey, OAuth.

Attributions

Attribution.

License

Apache — see LICENSE.

About

Simple and opinionated web framework written in zig

Topics

Resources

Stars

28 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages