Experimental - under active development
A JavaScript runtime
built for speed
Built from scratch in Zig for serverless workloads. One binary, zero dependencies, instant cold starts.
$ zig build -Doptimize=ReleaseFast
$ ./zig-out/bin/zigttp-server -e "function handler(r) { return Response.json({hello:'world'}) }"
listening on http://127.0.0.1:8080
$ curl http://localhost:8080/
{"hello":"world"}
What makes it different
Where Node.js and Deno optimize for generality, zigttp optimizes for a single use case: running a request handler as fast as possible, then getting out of the way.
Opinionated subset
TypeScript with the footguns removed. No classes, no this,
no var, no while loops. Unsupported features fail
at parse time with a suggested alternative, not at runtime with a cryptic stack trace.
JSX as a first-class primitive
The parser handles JSX and TSX directly. No Babel, no build step, no transpilation. Write server-rendered HTML with components and get back a string.
Compile-time evaluation
comptime() folds expressions at load time, modeled after Zig's comptime.
Hash a version string, uppercase a constant, precompute a config value -
all before the handler runs.
Native modules over polyfills
Common FaaS needs - JWT auth, JSON Schema validation, caching, crypto -
are implemented in Zig and exposed as zigttp:* virtual modules
with zero interpretation overhead.
Clean, predictable code
Functions in, responses out. No middleware chains, no decorator magic, no hidden state.
function handler(request) {
if (request.url === "/api/health") {
return Response.json({
status: "ok",
runtime: "zts",
timestamp: Date.now(),
});
}
return Response.json(
{ error: "Not Found" },
{ status: 404 }
);
}
function HomePage() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
<p>Welcome to zigttp!</p>
</body>
</html>
);
}
function handler(request) {
if (request.url === "/") {
return Response.html(renderToString(<HomePage />));
}
return Response.text("Not Found", { status: 404 });
}
import { routerMatch } from "zigttp:router";
const routes = {
"GET /": getHome,
"GET /health": getHealth,
"GET /users/:id": getUser,
"POST /echo": postEcho,
};
function handler(req) {
const match = routerMatch(routes, req);
if (match) {
req.params = match.params;
return match.handler(req);
}
return Response.json(
{ error: "Not Found" },
{ status: 404 }
);
}
import { parseBearer, jwtVerify } from "zigttp:auth";
import { schemaCompile, validateJson } from "zigttp:validate";
schemaCompile("user", JSON.stringify({
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", minLength: 5 },
}
}));
function handler(req) {
const token = parseBearer(req.headers["authorization"]);
if (!token) return Response.json(
{ error: "unauthorized" }, { status: 401 }
);
const auth = jwtVerify(token, "my-secret");
if (!auth.ok) return Response.json(
{ error: auth.error }, { status: 401 }
);
if (req.method === "POST") {
const result = validateJson("user", req.body);
if (!result.ok) return Response.json(
{ errors: result.errors }, { status: 400 }
);
return Response.json({ user: result.value }, { status: 201 });
}
return Response.json({ user: auth.value });
}
function TodoForm() {
return (
<form
hx-post="/todos"
hx-target="#todo-list"
hx-swap="beforeend">
<input type="text" name="text" required />
<button type="submit">Add Todo</button>
</form>
);
}
function handler(request) {
if (request.url === "/" && request.method === "GET") {
return Response.html(
renderToString(<TodoForm />)
);
}
if (request.url === "/todos" && request.method === "POST") {
const html = renderToString(
<div class="todo-item">New todo</div>
);
return Response.html(html);
}
return Response.text("Not Found", { status: 404 });
}
Performance
Powered by zts - a pure Zig JavaScript engine with a JIT compiler, NaN-boxed values, and hidden classes. It skips everything a FaaS handler doesn't need.
JS Engine Performance (vs QuickJS)
Cold Start Comparison
Runtime initialization is only 3ms. macOS dynamic linker accounts for 80-90ms. Linux static builds target 18-33ms total cold start.
Two-layer architecture
zigttp is a server. zts is the engine. Both are written in Zig. Together they form a purpose-built runtime for handling HTTP requests with JavaScript.
zigttp server
- HTTP/1.1 listener and parser
- HandlerPool with lock-free slot acquisition
- Static file serving with LRU cache
- CLI with inline eval, CORS, memory limits
- Build-time handler precompilation
- Native outbound HTTP bridge
zts engine
- Two-pass compiler (parse to IR, then bytecode)
- Stack-based VM with baseline JIT (x86-64, ARM64)
- NaN-boxed values with hidden classes
- Polymorphic inline cache (8-entry PIC)
- Generational GC with hybrid arena allocation
- Native TypeScript/TSX stripping
Virtual modules
Import native Zig implementations with zero JS interpretation overhead. Common FaaS functionality, none of the npm baggage.
zigttp:auth
JWT signing and verification (HS256), bearer token parsing, webhook signature verification, timing-safe comparison.
parseBearer, jwtVerify, jwtSign, verifyWebhookSignature
zigttp:validate
JSON Schema validation with compile-once-validate-many pattern. Supports type, required, properties, min/max, enum.
schemaCompile, validateJson, validateObject, coerceJson
zigttp:cache
In-memory key-value store with namespace isolation, TTL expiry, LRU eviction. Persists across requests in the same pool slot.
cacheGet, cacheSet, cacheDelete, cacheIncr, cacheStats
zigttp:crypto
SHA-256 hashing, HMAC-SHA256, base64 encoding and decoding. All implemented in Zig with no OpenSSL dependency.
sha256, hmacSha256, base64Encode, base64Decode
zigttp:router
Pattern-matching HTTP router with path parameter extraction. Declare routes as an object, match with a single call.
routerMatch
zigttp:env
Environment variable access for configuration. Simple key-value interface for deployment secrets and config.
env
Get started
Requires Zig 0.16.0 or later.
1 Build
$ git clone https://github.com/srdjan/zigttp
$ cd zigttp
$ zig build -Doptimize=ReleaseFast
2 Run
# Inline handler
$ ./zig-out/bin/zigttp-server -e "function handler(r) { return Response.json({ok:true}) }"
# Or with a file
$ ./zig-out/bin/zigttp-server examples/handler.jsx
3 Deploy
# Production build with embedded handler
$ zig build -Doptimize=ReleaseFast -Dhandler=handler.js
# Container: scratch base, 1.2MB total
$ docker build -t myapp .
The language subset
zts implements ES5 with select ES6+ extensions. Every unsupported feature is detected at parse time with a helpful error message suggesting an alternative.
Supported
- Strict mode, let/const
- Arrow functions
- Template literals
- Destructuring, spread
- for...of (arrays)
- Optional chaining, nullish coalescing
- Typed arrays
- JSX/TSX (native parsing)
- TypeScript type stripping
- comptime() evaluation
- range(start, end, step)
Intentionally excluded
class- use functions and objectsthis/new- use closuresvar- use let/constwhile- use for...of with range()try/catch- use Result typesasync/await- sequential by designPromises- one request at a time- Regular expressions
- Event loop
require/ dynamic import