Wiki Journal

Write a simple HTTP Client in Zig

Introduction

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.

Aside

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.

What you'll learn

The Code

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, .{});
        }
    };
};

Last built on 2025-02-12