Criar servidores HTTP é essencial para desenvolvimento backend moderno. Com Zig, você pode construir servidores HTTP de alta performance sem dependências externas, usando apenas a biblioteca padrão. Neste tutorial, vamos criar desde um servidor simples até uma API REST completa com roteamento, JSON e boas práticas.
Introdução ao std.http.Server
Por Zig para Backend?
Zig oferece vantagens únicas para desenvolvimento web:
- Performance nativa: Sem garbage collection, com controle preciso de alocações
- Footprint pequeno: Binários pequenos, ideal para containers
- Segurança de memória: Eliminação de vulnerabilidades comuns (buffer overflows)
- Cross-compilation: Compile para qualquer plataforma facilmente
- Sem dependências: std.http.Server está na biblioteca padrão
Arquitetura Básica
┌─────────────────────────────────────┐
│ std.http.Server │ ← Escuta conexões
├─────────────────────────────────────┤
│ Accept Connection │ ← Aceita novas conexões
├─────────────────────────────────────┤
│ Handle Request → Send Response │ ← Processa e responde
├─────────────────────────────────────┤
│ std.http.Server.Request/Response │ ← Tipos de requisição/resposta
└─────────────────────────────────────┘
Servidor HTTP Básico
Olá, Mundo Web
Vamos começar com o servidor mais simples possível:
const std = @import("std");
pub fn main() !void {
// Allocator para o servidor
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar o servidor na porta 8080
const address = std.net.Address.parseIp4("0.0.0.0", 8080) catch |err| {
std.debug.print("Erro ao parsear endereço: {}\n", .{err});
return err;
};
var server = try address.listen(.{
.reuse_address = true,
});
defer server.deinit();
std.debug.print("Servidor rodando em http://localhost:8080\n", .{});
// Loop principal — aceita conexões indefinidamente
while (true) {
const conn = try server.accept();
// Processar conexão
try handleConnection(allocator, conn);
}
}
fn handleConnection(allocator: std.mem.Allocator, conn: std.net.Server.Connection) !void {
defer conn.stream.close();
// Criar reader/writer HTTP
var read_buffer: [1024]u8 = undefined;
const reader = conn.stream.reader();
const writer = conn.stream.writer();
// Parse da requisição HTTP
const request_line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse return;
// Parse simples: "GET / HTTP/1.1"
var parts = std.mem.split(u8, request_line, " ");
const method = parts.next() orelse return;
const path = parts.next() orelse return;
std.debug.print("{s} {s}\n", .{ method, path });
// Responder
const response = "HTTP/1.1 200 OK\r\n" ++
"Content-Type: text/plain\r\n" ++
"Content-Length: 13\r\n" ++
"\r\n" ++
"Ola, Mundo!";
try writer.writeAll(response);
}
Testando o Servidor
# Compilar
zig build-exe server_simples.zig
# Executar
./server_simples
# Em outro terminal, teste:
curl http://localhost:8080
# Output: Ola, Mundo!
Usando std.http.Server
Servidor HTTP Completo
O código anterior usava TCP diretamente. Vamos usar std.http.Server que é mais robusto:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar servidor
const address = try std.net.Address.parseIp4("127.0.0.1", 8080);
var server = try address.listen(.{
.reuse_address = true,
});
defer server.deinit();
std.debug.print("Servidor em http://127.0.0.1:8080\n", .{});
// Buffer para headers
var header_buffer: [8192]u8 = undefined;
while (true) {
const conn = try server.accept();
// Criar conexão HTTP
var http_conn = std.http.Server.init(conn, &header_buffer);
// Receber requisição
var request = try http_conn.receiveHead();
// Responder
try sendResponse(conn.stream.writer(), "Hello from std.http.Server!");
}
}
fn sendResponse(writer: anytype, body: []const u8) !void {
const response = try std.fmt.allocPrint(
std.heap.page_allocator,
"HTTP/1.1 200 OK\r\n" ++
"Content-Type: text/plain\r\n" ++
"Content-Length: {d}\r\n" ++
"\r\n" ++
"{s}",
.{ body.len, body }
);
defer std.heap.page_allocator.free(response);
try writer.writeAll(response);
}
Roteamento Básico
Implementando Rotas Simples
Um servidor real precisa rotear requisições para diferentes handlers:
const std = @import("std");
const Route = struct {
method: []const u8,
path: []const u8,
handler: *const fn ([]const u8, std.net.Stream.Writer) anyerror!void,
};
fn homeHandler(_: []const u8, writer: std.net.Stream.Writer) !void {
const body = "Bem-vindo a API Zig!";
try sendResponse(writer, 200, "OK", "text/plain", body);
}
fn usersHandler(_: []const u8, writer: std.net.Stream.Writer) !void {
const body = "[{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}]";
try sendResponse(writer, 200, "OK", "application/json", body);
}
fn aboutHandler(_: []const u8, writer: std.net.Stream.Writer) !void {
const body = "API desenvolvida em Zig";
try sendResponse(writer, 200, "OK", "text/plain", body);
}
fn notFoundHandler(_: []const u8, writer: std.net.Stream.Writer) !void {
const body = "404 - Rota nao encontrada";
try sendResponse(writer, 404, "Not Found", "text/plain", body);
}
const routes = [_]Route{
.{ .method = "GET", .path = "/", .handler = homeHandler },
.{ .method = "GET", .path = "/users", .handler = usersHandler },
.{ .method = "GET", .path = "/about", .handler = aboutHandler },
};
fn sendResponse(
writer: std.net.Stream.Writer,
status_code: u16,
status_text: []const u8,
content_type: []const u8,
body: []const u8,
) !void {
const response = try std.fmt.allocPrint(
std.heap.page_allocator,
"HTTP/1.1 {d} {s}\r\n" ++
"Content-Type: {s}\r\n" ++
"Content-Length: {d}\r\n" ++
"\r\n" ++
"{s}",
.{ status_code, status_text, content_type, body.len, body }
);
defer std.heap.page_allocator.free(response);
try writer.writeAll(response);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const address = try std.net.Address.parseIp4("127.0.0.1", 8080);
var server = try address.listen(.{
.reuse_address = true,
.kernel_backlog = 128,
});
defer server.deinit();
std.debug.print("API em http://127.0.0.1:8080\n", .{});
std.debug.print("Rotas:\n", .{});
for (routes) |route| {
std.debug.print(" {s} {s}\n", .{ route.method, route.path });
}
var read_buffer: [1024]u8 = undefined;
while (true) {
const conn = try server.accept();
defer conn.stream.close();
const reader = conn.stream.reader();
const writer = conn.stream.writer();
// Ler requisição
const request_line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse continue;
// Parse
var parts = std.mem.split(u8, request_line, " ");
const method = parts.next() orelse continue;
const path = parts.next() orelse continue;
// Ignorar headers (para este exemplo simples)
while (true) {
const line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse break;
if (line.len == 1 or (line.len == 2 and line[0] == '\r')) break;
}
// Roteamento
var found = false;
for (routes) |route| {
if (std.mem.eql(u8, route.method, method) and
std.mem.eql(u8, route.path, path))
{
try route.handler(path, writer);
found = true;
break;
}
}
if (!found) {
try notFoundHandler(path, writer);
}
}
}
API REST com JSON
Servidor CRUD Completo
Vamos criar uma API REST para gerenciar tarefas (todo list):
const std = @import("std");
// Modelo
const Task = struct {
id: u32,
title: []const u8,
completed: bool,
};
// Estado global (simplificado — em produção use banco de dados)
var tasks = std.ArrayList(Task).init(std.heap.page_allocator);
var next_id: u32 = 1;
const Context = struct {
allocator: std.mem.Allocator,
reader: std.net.Stream.Reader,
writer: std.net.Stream.Writer,
method: []const u8,
path: []const u8,
body: ?[]const u8,
};
// Handler para GET /tasks
fn getTasks(ctx: Context) !void {
// Serializar tasks para JSON
var json = std.ArrayList(u8).init(ctx.allocator);
defer json.deinit();
try json.append('[');
for (tasks.items, 0..) |task, i| {
if (i > 0) try json.appendSlice(",");
const task_json = try std.fmt.allocPrint(
ctx.allocator,
"{{\"id\":{d},\"title\":\"{s}\",\"completed\":{}}}",
.{ task.id, task.title, task.completed }
);
defer ctx.allocator.free(task_json);
try json.appendSlice(task_json);
}
try json.append(']');
try sendJson(ctx.writer, 200, json.items);
}
// Handler para GET /tasks/:id
fn getTask(ctx: Context, id: u32) !void {
for (tasks.items) |task| {
if (task.id == id) {
const json = try std.fmt.allocPrint(
ctx.allocator,
"{{\"id\":{d},\"title\":\"{s}\",\"completed\":{}}}",
.{ task.id, task.title, task.completed }
);
defer ctx.allocator.free(json);
try sendJson(ctx.writer, 200, json);
return;
}
}
try sendError(ctx.writer, 404, "Task not found");
}
// Handler para POST /tasks
fn createTask(ctx: Context) !void {
const body = ctx.body orelse {
try sendError(ctx.writer, 400, "Body required");
return;
};
// Parse JSON simples (em produção use std.json.parse)
const title = extractJsonString(body, "title") orelse {
try sendError(ctx.writer, 400, "Title required");
return;
};
const task = Task{
.id = next_id,
.title = try ctx.allocator.dupe(u8, title),
.completed = false,
};
next_id += 1;
try tasks.append(task);
const json = try std.fmt.allocPrint(
ctx.allocator,
"{{\"id\":{d},\"title\":\"{s}\",\"completed\":false}}",
.{ task.id, task.title }
);
defer ctx.allocator.free(json);
try sendJson(ctx.writer, 201, json);
}
// Handler para PUT /tasks/:id
fn updateTask(ctx: Context, id: u32) !void {
const body = ctx.body orelse {
try sendError(ctx.writer, 400, "Body required");
return;
};
for (tasks.items) |*task| {
if (task.id == id) {
if (extractJsonString(body, "title")) |new_title| {
task.title = new_title;
}
if (extractJsonBool(body, "completed")) |new_completed| {
task.completed = new_completed;
}
const json = try std.fmt.allocPrint(
ctx.allocator,
"{{\"id\":{d},\"title\":\"{s}\",\"completed\":{}}}",
.{ task.id, task.title, task.completed }
);
defer ctx.allocator.free(json);
try sendJson(ctx.writer, 200, json);
return;
}
}
try sendError(ctx.writer, 404, "Task not found");
}
// Handler para DELETE /tasks/:id
fn deleteTask(ctx: Context, id: u32) !void {
for (tasks.items, 0..) |task, i| {
if (task.id == id) {
_ = tasks.orderedRemove(i);
try sendJson(ctx.writer, 204, "");
return;
}
}
try sendError(ctx.writer, 404, "Task not found");
}
// Helpers
fn sendJson(writer: std.net.Stream.Writer, status: u16, body: []const u8) !void {
const response = try std.fmt.allocPrint(
std.heap.page_allocator,
"HTTP/1.1 {d} {s}\r\n" ++
"Content-Type: application/json\r\n" ++
"Content-Length: {d}\r\n" ++
"\r\n" ++
"{s}",
.{ status, if (status == 200) "OK" else if (status == 201) "Created" else "No Content", body.len, body }
);
defer std.heap.page_allocator.free(response);
try writer.writeAll(response);
}
fn sendError(writer: std.net.Stream.Writer, status: u16, message: []const u8) !void {
const json = try std.fmt.allocPrint(
std.heap.page_allocator,
"{{\"error\":\"{s}\"}}",
.{message}
);
defer std.heap.page_allocator.free(json);
const response = try std.fmt.allocPrint(
std.heap.page_allocator,
"HTTP/1.1 {d} Error\r\n" ++
"Content-Type: application/json\r\n" ++
"Content-Length: {d}\r\n" ++
"\r\n" ++
"{s}",
.{ status, json.len, json }
);
defer std.heap.page_allocator.free(response);
try writer.writeAll(response);
}
fn extractJsonString(json: []const u8, key: []const u8) ?[]const u8 {
const pattern = try std.fmt.allocPrint(
std.heap.page_allocator,
"\"{s}\":\"",
.{key}
);
defer std.heap.page_allocator.free(pattern);
if (std.mem.indexOf(u8, json, pattern)) |start| {
const value_start = start + pattern.len;
if (std.mem.indexOfScalar(u8, json[value_start..], '"')) |end| {
return json[value_start .. value_start + end];
}
}
return null;
}
fn extractJsonBool(json: []const u8, key: []const u8) ?bool {
const pattern = try std.fmt.allocPrint(
std.heap.page_allocator,
"\"{s}\":",
.{key}
);
defer std.heap.page_allocator.free(pattern);
if (std.mem.indexOf(u8, json, pattern)) |start| {
const value_start = start + pattern.len;
if (std.mem.startsWith(u8, json[value_start..], "true")) {
return true;
} else if (std.mem.startsWith(u8, json[value_start..], "false")) {
return false;
}
}
return null;
}
// Roteamento
fn handleRequest(ctx: Context) !void {
if (std.mem.eql(u8, ctx.path, "/tasks")) {
if (std.mem.eql(u8, ctx.method, "GET")) {
try getTasks(ctx);
} else if (std.mem.eql(u8, ctx.method, "POST")) {
try createTask(ctx);
} else {
try sendError(ctx.writer, 405, "Method not allowed");
}
} else if (std.mem.startsWith(u8, ctx.path, "/tasks/")) {
const id_str = ctx.path[7..];
const id = std.fmt.parseInt(u32, id_str, 10) catch {
try sendError(ctx.writer, 400, "Invalid ID");
return;
};
if (std.mem.eql(u8, ctx.method, "GET")) {
try getTask(ctx, id);
} else if (std.mem.eql(u8, ctx.method, "PUT")) {
try updateTask(ctx, id);
} else if (std.mem.eql(u8, ctx.method, "DELETE")) {
try deleteTask(ctx, id);
} else {
try sendError(ctx.writer, 405, "Method not allowed");
}
} else {
try sendError(ctx.writer, 404, "Not found");
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const address = try std.net.Address.parseIp4("127.0.0.1", 8080);
var server = try address.listen(.{
.reuse_address = true,
.kernel_backlog = 128,
});
defer server.deinit();
std.debug.print("API REST em http://127.0.0.1:8080\n", .{});
std.debug.print("\nEndpoints:\n", .{});
std.debug.print(" GET /tasks - Listar tarefas\n", .{});
std.debug.print(" GET /tasks/:id - Obter tarefa\n", .{});
std.debug.print(" POST /tasks - Criar tarefa\n", .{});
std.debug.print(" PUT /tasks/:id - Atualizar tarefa\n", .{});
std.debug.print(" DELETE /tasks/:id - Remover tarefa\n", .{});
var read_buffer: [4096]u8 = undefined;
while (true) {
const conn = try server.accept();
// Ler requisição
const reader = conn.stream.reader();
const writer = conn.stream.writer();
const request_line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse {
conn.stream.close();
continue;
};
var parts = std.mem.split(u8, request_line, " ");
const method = parts.next() orelse {
conn.stream.close();
continue;
};
const path = parts.next() orelse {
conn.stream.close();
continue;
};
// Ler headers e body
var content_length: usize = 0;
while (true) {
const line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse break;
if (line.len == 1 or (line.len == 2 and line[0] == '\r')) break;
if (std.mem.startsWith(u8, line, "Content-Length:")) {
const val = std.mem.trim(u8, line[15..], " \r\n");
content_length = std.fmt.parseInt(usize, val, 10) catch 0;
}
}
// Ler body se houver
var body: ?[]const u8 = null;
if (content_length > 0) {
const body_buf = try allocator.alloc(u8, content_length);
_ = try reader.readAll(body_buf);
body = body_buf;
}
const ctx = Context{
.allocator = allocator,
.reader = reader,
.writer = writer,
.method = method,
.path = path,
.body = body,
};
handleRequest(ctx) catch |err| {
std.debug.print("Erro: {}\n", .{err});
};
if (body) |b| allocator.free(b);
conn.stream.close();
}
}
Testando a API
# Iniciar servidor
zig run api.zig
# Listar tarefas (vazio inicialmente)
curl http://localhost:8080/tasks
# Output: []
# Criar tarefa
curl -X POST http://localhost:8080/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Aprender Zig"}'
# Output: {"id":1,"title":"Aprender Zig","completed":false}
# Criar outra
curl -X POST http://localhost:8080/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Criar API"}'
# Listar todas
curl http://localhost:8080/tasks
# Output: [{"id":1,...},{"id":2,...}]
# Atualizar
curl -X PUT http://localhost:8080/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'
# Deletar
curl -X DELETE http://localhost:8080/tasks/2
Servindo Arquivos Estáticos
Static File Server
const std = @import("std");
const MIME_TYPES = std.StaticStringMap([]const u8).initComptime(.{
.{ ".html", "text/html" },
.{ ".css", "text/css" },
.{ ".js", "application/javascript" },
.{ ".json", "application/json" },
.{ ".png", "image/png" },
.{ ".jpg", "image/jpeg" },
.{ ".gif", "image/gif" },
.{ ".svg", "image/svg+xml" },
.{ ".ico", "image/x-icon" },
});
fn serveFile(writer: std.net.Stream.Writer, path: []const u8) !void {
// Sanitizar path
if (std.mem.indexOf(u8, path, "..") != null) {
try sendError(writer, 403, "Forbidden");
return;
}
// Default para index.html
const file_path = if (std.mem.eql(u8, path, "/"))
"public/index.html"
else
try std.fmt.allocPrint(
std.heap.page_allocator,
"public{s}",
.{path}
);
const file = std.fs.cwd().openFile(file_path, .{}) catch {
try sendError(writer, 404, "Not found");
return;
};
defer file.close();
const content = file.readToEndAlloc(std.heap.page_allocator, 10 * 1024 * 1024) catch {
try sendError(writer, 500, "Internal error");
return;
};
defer std.heap.page_allocator.free(content);
// Detectar MIME type
const ext = std.fs.path.extension(file_path);
const mime = MIME_TYPES.get(ext) orelse "application/octet-stream";
try sendResponse(writer, 200, "OK", mime, content);
}
fn sendResponse(
writer: std.net.Stream.Writer,
status: u16,
status_text: []const u8,
content_type: []const u8,
body: []const u8,
) !void {
const response = try std.fmt.allocPrint(
std.heap.page_allocator,
"HTTP/1.1 {d} {s}\r\n" ++
"Content-Type: {s}\r\n" ++
"Content-Length: {d}\r\n" ++
"\r\n",
.{ status, status_text, content_type, body.len }
);
defer std.heap.page_allocator.free(response);
try writer.writeAll(response);
try writer.writeAll(body);
}
fn sendError(writer: std.net.Stream.Writer, status: u16, message: []const u8) !void {
try sendResponse(writer, status, message, "text/plain", message);
}
pub fn main() !void {
const address = try std.net.Address.parseIp4("127.0.0.1", 8080);
var server = try address.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print("Static server em http://127.0.0.1:8080\n", .{});
std.debug.print("Coloque arquivos em ./public/\n", .{});
var read_buffer: [1024]u8 = undefined;
while (true) {
const conn = try server.accept();
defer conn.stream.close();
const reader = conn.stream.reader();
const writer = conn.stream.writer();
const request_line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse continue;
var parts = std.mem.split(u8, request_line, " ");
const method = parts.next() orelse continue;
const path = parts.next() orelse continue;
// Ignorar headers
while (true) {
const line = try reader.readUntilDelimiterOrEof(
&read_buffer,
'\n'
) orelse break;
if (line.len <= 2) break;
}
if (std.mem.eql(u8, method, "GET")) {
try serveFile(writer, path);
} else {
try sendError(writer, 405, "Method not allowed");
}
}
}
Middleware e Logging
Adicionando Logging
fn logRequest(method: []const u8, path: []const u8, status: u16, duration_ms: u64) void {
const timestamp = std.time.timestamp();
const datetime = timestampToDateTime(timestamp);
std.debug.print(
"[{d:0>4}-{d:0>2}-{d:0>2} {d:0>2}:{d:0>2}:{d:0>2}] ",
.{ datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second }
);
std.debug.print("{s} {s} - {d} ({d}ms)\n", .{ method, path, status, duration_ms });
}
fn timestampToDateTime(timestamp: i64) struct { year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8 } {
// Simplificação — use biblioteca de data real em produção
const seconds_per_day = 86400;
const days = @divFloor(timestamp, seconds_per_day);
const seconds_of_day = @mod(timestamp, seconds_per_day);
const hour = @intCast(u8, @divFloor(seconds_of_day, 3600));
const minute = @intCast(u8, @mod(@divFloor(seconds_of_day, 60), 60));
const second = @intCast(u8, @mod(seconds_of_day, 60));
// Aproximação simples (não considera anos bissextos)
const year = 1970 + @intCast(u16, @divFloor(days, 365));
const day_of_year = @mod(days, 365);
const month = @intCast(u8, @divFloor(day_of_year, 30) + 1);
const day = @intCast(u8, @mod(day_of_year, 30) + 1);
return .{
.year = year,
.month = month,
.day = day,
.hour = hour,
.minute = minute,
.second = second,
};
}
Melhores Práticas
1. Sempre Use defer para Cleanup
const conn = try server.accept();
defer conn.stream.close(); // Sempre fecha a conexão
2. Limite o Tamanho dos Buffers
// ✅ Bom: buffer limitado
var body_buf = try allocator.alloc(u8, content_length);
if (content_length > 10 * 1024 * 1024) {
return error.BodyTooLarge;
}
// ❌ Ruim: aceitar qualquer tamanho
3. Sanitize Inputs
// ✅ Verificar path traversal
if (std.mem.indexOf(u8, path, "..") != null) {
return error.Forbidden;
}
4. Use Allocators Apropriados
// ✅ GPA para debug, PageAllocator/FixedBuffer para produção
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// Em produção: considere FixedBufferAllocator ou Arena
5. Trate Erros Gracefully
handleRequest(ctx) catch |err| {
std.log.err("Erro na requisição: {}", .{err});
sendError(writer, 500, "Internal Server Error") catch {};
};
Build e Deploy
build.zig para Servidor Web
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "zig-server",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "Run the server");
run_step.dependOn(&run_cmd.step);
}
Docker para Deploy
# Dockerfile
FROM alpine:latest AS builder
RUN apk add --no-cache curl
WORKDIR /app
RUN curl -L https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz | tar xJ
ENV PATH="/app/zig-linux-x86_64-0.13.0:${PATH}"
COPY . .
RUN zig build -Doptimize=ReleaseSafe
FROM scratch
COPY --from=builder /app/zig-out/bin/zig-server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
Cross-Compilation
# Linux x86_64
zig build -Dtarget=x86_64-linux-gnu -Doptimize=ReleaseSafe
# Linux ARM64 (Raspberry Pi, AWS Graviton)
zig build -Dtarget=aarch64-linux-gnu -Doptimize=ReleaseSafe
# Windows
zig build -Dtarget=x86_64-windows-gnu -Doptimize=ReleaseSafe
# macOS
zig build -Dtarget=x86_64-macos-none -Doptimize=ReleaseSafe
zig build -Dtarget=aarch64-macos-none -Doptimize=ReleaseSafe
Comparação com Outras Linguagens
| Aspecto | Zig | Node.js | Go | Rust |
|---|---|---|---|---|
| Deps HTTP | 0 (stdlib) | 0 (built-in) | 0 (stdlib) | 1 (hyper/axum) |
| Binary size | ~100KB | 50MB+ (com runtime) | ~2MB | ~1MB |
| Memory | Manual | GC | GC | Ownership |
| Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Complexidade | Média | Baixa | Baixa | Alta |
Próximos Passos
Agora que você sabe criar servidores HTTP em Zig:
- HTTP Client em Zig — Consuma outras APIs do seu servidor
- Parsing JSON em Zig — APIs REST geralmente usam JSON
- Zig e C: Interoperabilidade — Use bibliotecas C avançadas
- Async/Await em Zig — Servidores concorrentes
Construa APIs rápidas e seguras com Zig. Compartilhe seus projetos!