5.1 KiB
⚡zap⚡ - blazingly fast backends
Zap is intended to become the zig replacement for the kind of REST APIs I used to write in python with Flask and mongodb, etc.
What I need for that is a blazingly fast, robust HTTP server that I can use with zig. While facil.io supports TLS, I don't mind HTTPS. In production, I use nginx as a reverse proxy anyway.
Zap wraps and patches facil.io - the C web application framework.
⚡ZAP⚡ IS SUPER ALPHA
Here's what works:
- Super easy build process: zag's
build.zig
fetches git sub-modules, applies a patch to facil.io's logging for microsecond precision, builds and optionally runs everything.- tested on Linux and macOS (arm, M1)
- hello: welcomes you with some static HTML
- routes: a super easy example dispatching on the HTTP path
- serve: the traditional static web server with optional dynamic request handling
I'll continue wrapping more of facil.io's functionality and adding stuff to zap to a point where I can use it as the JSON REST API backend for my current research project which is likely to need to go live in the summer of 2023.
Getting started
$ git clone https://github.com/renerocksai/zap.git
$ cd zap
$ zig build run-hello
$ # open http://localhost:3000 in your browser
... and open http://localhost:3000 in your browser.
Examples
You build and run the examples via:
$ zig build hello [EXAMPLE]
$ ./zig-out/bin/[EXAMPLE]
... where [EXAMPLE]
is one of hello
, routes
, or serve
.
Example: building and running the hello example:
$ zig build hello
$ ./zig-out/bin/hello
To just run an example, like routes
, without generating an executable, run:
$ zig build run-[EXAMPLE]
Example: building and running the routes example:
$ zig build run-routes
hello
const std = @import("std");
const zap = @import("zap");
fn on_request(r: zap.SimpleRequest) void {
if (r.path) |the_path| {
std.debug.print("PATH: {s}\n", .{the_path});
}
if (r.query) |the_query| {
std.debug.print("QUERY: {s}\n", .{the_query});
}
_ = r.sendBody("<html><body><h1>Hello from ZAP!!!</h1></body></html>");
}
pub fn main() !void {
var listener = zap.SimpleHttpListener.init(.{
.port = 3000,
.on_request = on_request,
.log = false,
});
try listener.listen();
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
// start worker threads
zap.start(.{
.threads = 2,
.workers = 2,
});
}
serve
const std = @import("std");
const zap = @import("zap");
fn on_request(r: zap.SimpleRequest) void {
// TODO: send 404 response
_ = r.sendBody("<html><body><h1>404 - File not found</h1></body></html>");
}
pub fn main() !void {
var listener = zap.SimpleHttpListener.init(.{
.port = 3000,
.on_request = on_request,
.public_folder = std.mem.span("examples/serve"),
.log = true,
});
try listener.listen();
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
// start worker threads
zap.start(.{
.threads = 2,
.workers = 2,
});
}
routes
const std = @import("std");
const zap = @import("zap");
fn dispatch_routes(r: zap.SimpleRequest) void {
// dispatch
if (r.path) |the_path| {
if (routes.get(the_path)) |foo| {
foo(r);
}
}
// or default: present menu
_ = r.sendBody(
\\ <html>
\\ <body>
\\ <p><a href="/static">static</a></p>
\\ <p><a href="/dynamic">dynamic</a></p>
\\ </body>
\\ </html>
);
}
fn static_site(r: zap.SimpleRequest) void {
_ = r.sendBody("<html><body><h1>Hello from STATIC ZAP!</h1></body></html>");
}
var dynamic_counter: i32 = 0;
fn dynamic_site(r: zap.SimpleRequest) void {
dynamic_counter += 1;
var buf: [128]u8 = undefined;
const filled_buf = std.fmt.bufPrintZ(
&buf,
"<html><body><h1>Hello # {d} from DYNAMIC ZAP!!!</h1></body></html>",
.{dynamic_counter},
) catch "ERROR";
_ = r.sendBody(std.mem.span(filled_buf));
}
fn setup_routes(a: std.mem.Allocator) !void {
routes = std.StringHashMap(zap.SimpleHttpRequestFn).init(a);
try routes.put("/static", static_site);
try routes.put("/dynamic", dynamic_site);
}
var routes: std.StringHashMap(zap.SimpleHttpRequestFn) = undefined;
pub fn main() !void {
try setup_routes(std.heap.page_allocator);
var listener = zap.SimpleHttpListener.init(.{
.port = 3000,
.on_request = dispatch_routes,
.log = true,
});
try listener.listen();
std.debug.print("Listening on 0.0.0.0:3000\n", .{});
zap.start(.{
.threads = 2,
.workers = 2,
});
}