It's fairly simple to start making HTTP requests from Zig, with the standard library's std.http.Client. Today, we're going to use Zig to interact with the JSONPlaceholder REST API.
This blog post assumes familiarity with Zig. It does not try to convince you why you should be using Zig or to explain its design choices.
If this is your first time hearing about it, it's recommended you go straight to Zig's website to learn more about it there.
If you've heard of Zig before but haven't installed it yet, consider this your chance to install it before following along.
- std.json.stringify We will use this to turn a Zig struct instance into a JSON string.
- std.json.parseFromSliceLeaky We will use this to turn a JSON string into a Zig struct instance. Note that we are using the "leaky" variant in conjunction with an ArenaAllocator.
- std.heap.ArenaAllocator This will be used to keep the JSON parsing memory management strategy simple.
- std.http.Client.fetch This will perform the HTTP connection, send the request, and store the response.
- std.ArrayListUnmanaged This will be used as a buffer to store the response.
const std = @import("std");
const heap = std.heap;
const http = std.http;
const json = std.json;
const mem = std.mem;
pub fn main() !void {
var gpa: heap.GeneralPurposeAllocator(.{}) = .{};
defer _ = gpa.deinit();
var arena = heap.ArenaAllocator.init(gpa.allocator());
defer arena.deinit();
const posts = try JSONPlaceholder.Client.getPosts(arena.allocator());
const stdout = std.io.getStdOut().writer();
for (posts) |post| {
try stdout.print(
\\📖 {d} - {s}
\\{s}
\\🖋️userId={d}
++ "\n\n",
.{ post.id, post.title, post.body, post.userId },
);
}
}
const JSONPlaceholder = struct {
pub const Posts = []const Post;
pub const Post = struct {
userId: u32,
id: u32,
title: []const u8,
body: []const u8,
};
pub const Client = struct {
pub fn getPosts(arena: mem.Allocator) !Posts {
var client: http.Client = .{ .allocator = arena };
defer client.deinit();
var buf = try std.ArrayListUnmanaged(u8).initCapacity(arena, 1024 * 1024);
errdefer buf.deinit(arena);
const result = try client.fetch(.{
.location = .{ .url = "https://jsonplaceholder.typicode.com/posts" },
.method = .GET,
.response_storage = .{ .static = &buf },
});
switch (result.status) {
.ok => {},
else => |s| {
// TODO: Error reporting...
std.log.err("Fetch request error: ({s}, {d})", .{ @tagName(s), s });
return error.FetchError;
},
}
return try json.parseFromSliceLeaky(Posts, arena, buf.items, .{});
}
};
};