Note: This project is experimental and under active development.
A serverless JavaScript runtime for FaaS (Function-as-a-Service) use cases, powered by zts - a pure Zig JavaScript engine. Designed for AWS Lambda, Azure Functions, Cloudflare Workers, and edge computing deployments.
- Instant cold starts: No JIT warm-up, predictable startup times
- Small deployment package: Pure Zig, zero external dependencies
- Request isolation: RuntimePool with pre-warmed contexts
- Functional API: Response helpers similar to Deno/Fetch API
- Safe by default: Strict mode JavaScript, sandboxed execution
- TypeScript/TSX: Native type stripping with compile-time evaluation
- AWS Lambda / Azure Functions / Cloudflare Workers style deployments
- Edge computing with JavaScript handlers
- Lightweight HTTP function handlers
- Multi-tenant request processing
zig build -Doptimize=ReleaseFast# Inline handler
./zig-out/bin/zigttp-server -e "function handler(r) { return Response.json({hello:'world'}) }"
# Or with a handler file
./zig-out/bin/zigttp-server examples/handler.js
# Test it
curl http://localhost:8080/zigttp-server [options] <handler.js>
zigttp-server -e "<inline-code>"
Options:
-p, --port <PORT> Port to listen on (default: 8080)
-h, --host <HOST> Host to bind to (default: 127.0.0.1)
-e, --eval <CODE> Evaluate inline JavaScript handler
-m, --memory <SIZE> JS runtime memory limit (default: 256k)
-q, --quiet Disable request logging
--help Show help message
Your handler must define a handler function that receives a request object and returns a Response.
{
method: string, // "GET", "POST", etc.
url: string, // URL path (e.g., "/api/users")
headers: object, // HTTP headers
body: string|null // Request body (for POST/PUT)
}// Basic response
new Response(body, { status: 200, headers: {} })
// JSON response (sets Content-Type automatically)
Response.json(data, init?)
// Text response
Response.text(text, init?)
// HTML response
Response.html(html, init?)function handler(request) {
// Simple routing
if (request.url === '/') {
return Response.html('<h1>Hello World</h1>');
}
if (request.url === '/api/echo') {
return Response.json({
method: request.method,
url: request.url,
body: request.body
});
}
if (request.method === 'POST' && request.url === '/api/data') {
var data = JSON.parse(request.body);
return Response.json({ received: data, ok: true });
}
// 404 fallback
return new Response('Not Found', { status: 404 });
}zigttp-server includes a native JSX transformer for server-side rendering. Use .jsx files to write handlers with JSX syntax.
// examples/jsx-simple.jsx
function handler(request) {
var page = <div class="hello">Hello JSX!</div>;
return Response.html(renderToString(page));
}function Card(props) {
return (
<div class="card">
<h2>{props.title}</h2>
<div>{props.children}</div>
</div>
);
}
function handler(request) {
var page = <Card title="Welcome">Hello from JSX!</Card>;
return Response.html(renderToString(page));
}h(tag, props, ...children)- Create virtual DOM node (used internally by transformer)renderToString(node)- Render virtual DOM to HTML stringFragment- Fragment component for grouping without wrapper element
| Feature | Example | Output |
|---|---|---|
| Elements | <div>text</div> |
<div>text</div> |
| Attributes | <div class="foo"> |
<div class="foo"> |
| Expressions | <div>{value}</div> |
<div>...</div> |
| Components | <Card title="x"/> |
Calls Card function |
| Fragments | <>a</> |
a (no wrapper) |
| Self-closing | <br/> |
<br /> |
| Boolean attrs | <input disabled/> |
<input disabled /> |
// examples/jsx-ssr.jsx
function Layout(props) {
return (
<html>
<head><title>{props.title}</title></head>
<body>
<h1>{props.title}</h1>
{props.children}
</body>
</html>
);
}
function handler(request) {
var page = (
<Layout title="My App">
<p>Method: {request.method}</p>
</Layout>
);
return Response.html(renderToString(page));
}zts includes a native TypeScript/TSX stripper that removes type annotations at load time. Use .ts or .tsx files directly without a separate build step.
// handler.ts
interface Request {
method: string;
path: string;
headers: Record<string, string>;
body: string | null;
}
function handler(request: Request): Response {
const data: { message: string } = { message: "Hello TypeScript!" };
return Response.json(data);
}The comptime() function evaluates expressions at load time and replaces them with literal values:
// Arithmetic
const x = comptime(1 + 2 * 3); // -> const x = 7;
// String operations
const upper = comptime("hello".toUpperCase()); // -> const upper = "HELLO";
// Math functions
const pi = comptime(Math.PI); // -> const pi = 3.141592653589793;
const max = comptime(Math.max(1, 5, 3)); // -> const max = 5;
// Hash function (FNV-1a)
const etag = comptime(hash("content-v1")); // -> const etag = "a1b2c3d4";
// JSON parsing
const cfg = comptime(JSON.parse('{"a":1}')); // -> const cfg = ({a:1});
// TSX works too
const el = <div>{comptime(1+2)}</div>; // -> <div>{3}</div>| Category | Operations |
|---|---|
| Literals | number, string, boolean, null, undefined, NaN, Infinity |
| Arithmetic | + - * / % ** |
| Bitwise | | & ^ << >> >>> |
| Comparison | == != === !== < <= > >= |
| Logical | && || ?? |
| Ternary | cond ? a : b |
| Math | PI, E, floor, ceil, round, sqrt, sin, cos, min, max, etc. |
| String | length, toUpperCase, toLowerCase, trim, slice, split, replace, etc. |
| Built-in | parseInt, parseFloat, JSON.parse, hash |
Disallowed: variables, Date.now(), Math.random(), closures, assignments.
See docs/typescript-comptime-spec.md for the full specification.
zts implements ES5 with some ES6+ extensions. Key limitations:
- Strict mode only: No
with, globals must be declared withvar - No array holes:
[1,,3]is a syntax error - No direct eval: Only global eval
(1, eval)('code') - No value boxing: No
new Number(1),new String('x') - Limited Date: Only
Date.now()is available
Supported ES6+ features:
for...of(arrays only)- Typed arrays
\u{hex}in stringsMath.imul,Math.clz32,Math.fround,Math.trunc- Exponentiation operator (
**) String.prototype.codePointAt,replaceAll,trimStart,trimEndglobalThis
┌─────────────────────────────────────────────────────────────┐
│ zigttp-server (Zig) │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ HTTP Server │──│ RuntimePool │──│ Native Bindings │ │
│ │ (std.net) │ │ (contexts) │ │ (console, Response) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ zts (Pure Zig) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Parser │──│ Bytecode │──│ Generational GC │ │
│ │ │ │ VM │ │ (Nursery + Tenured) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
zts uses a generational garbage collector with:
- NaN-boxing for efficient value representation (64-bit tagged values)
- Hidden classes for inline caching (V8-style optimization)
- RuntimePool for request isolation in FaaS environments
The Result pattern throughout makes error handling explicit and prevents silent failures.
zigttp-server/
├── build.zig # Zig build configuration
├── zts/ # Pure Zig JavaScript engine
│ ├── parser/ # Two-pass parser with IR
│ │ ├── parse.zig # Main parser (Pratt parser)
│ │ ├── tokenizer.zig # Tokenizer
│ │ ├── codegen.zig # Bytecode generation
│ │ └── ir.zig # Intermediate representation
│ ├── interpreter.zig # Stack-based bytecode VM
│ ├── value.zig # NaN-boxing value representation
│ ├── object.zig # Hidden classes, object system
│ ├── gc.zig # Generational GC (nursery + tenured)
│ ├── heap.zig # Size-class segregated allocator
│ ├── http.zig # HTTP/JSX runtime for SSR
│ ├── pool.zig # Lock-free runtime pooling
│ ├── builtins.zig # Built-in JavaScript functions
│ ├── stripper.zig # TypeScript/TSX type stripper
│ └── comptime.zig # Compile-time expression evaluator
├── src/
│ ├── main.zig # CLI entry point
│ ├── zruntime.zig # RuntimePool, JS context management
│ ├── server.zig # HTTP server implementation
│ ├── bindings.zig # Native APIs (console, fetch, Deno)
│ └── jsx.zig # JSX transformer
└── examples/
├── handler.jsx # Example JSX handler
├── htmx-todo/ # HTMX Todo app example
└── jsx-ssr.jsx # Full SSR example
- Zig 0.16.0 or later (nightly)
# Debug build
zig build
# Release build (optimized)
zig build -Doptimize=ReleaseFast
# Run tests
zig build test # Main runtime tests
zig build test-zts # JS engine tests
zig build test-zruntime # Native Zig runtime tests
# Run directly
zig build run -- -e "function handler(r) { return Response.json({ok:true}) }"Add custom native functions callable from JavaScript by implementing the NativeFn signature in zts/object.zig:
// In bindings.zig or a custom module:
fn myNativeFunction(ctx: *zts.Context, this: zts.JSValue, args: []const zts.JSValue) !zts.JSValue {
// Your implementation
return zts.JSValue.fromInt(42);
}
// Register it via context.setGlobal()See src/bindings.zig for examples of console, fetch, and Deno API implementations.
- Cold start: < 1ms to initialize runtime and load handler
- Warm invocations: RuntimePool reuses pre-warmed contexts
- Memory: 256KB default JS heap (configurable per function)
- Deployment size: ~500KB binary, zero runtime dependencies
# Single instance (Lambda-style)
./zigttp-server handler.js
# Multiple instances behind load balancer
# Each instance handles one request at a time for isolationFor high-throughput scenarios, deploy multiple instances. The small binary size and instant cold starts make horizontal scaling efficient.
MIT licensed.
- zts - Pure Zig JavaScript engine (part of this project)
- Zig programming language