├── README ├── build.zig ├── build.zig.zon └── src └── main.zig /README: -------------------------------------------------------------------------------- 1 | Simple fetch requests with Zig 2 | -------------------------- 3 | 4 | A simple program that can handle GET and POST requests. 5 | 6 | You can find the related Youtube video here: https://youtu.be/xSyYl186rLw 7 | 8 | Built for Version-0.12.0-dev.3318+6724a524d 9 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const exe = b.addExecutable(.{ 8 | .name = "req", 9 | .root_source_file = .{ .path = "src/main.zig" }, 10 | .target = target, 11 | .optimize = optimize, 12 | }); 13 | b.installArtifact(exe); 14 | 15 | const run_cmd = b.addRunArtifact(exe); 16 | run_cmd.step.dependOn(b.getInstallStep()); 17 | if (b.args) |args| { 18 | run_cmd.addArgs(args); 19 | } 20 | 21 | const run_step = b.step("run", "Run the app"); 22 | run_step.dependOn(&run_cmd.step); 23 | 24 | const exe_unit_tests = b.addTest(.{ 25 | .root_source_file = .{ .path = "src/main.zig" }, 26 | .target = target, 27 | .optimize = optimize, 28 | }); 29 | 30 | const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); 31 | 32 | // Similar to creating the run step earlier, this exposes a `test` step to 33 | // the `zig build --help` menu, providing a way for the user to request 34 | // running the unit tests. 35 | const test_step = b.step("test", "Run unit tests"); 36 | test_step.dependOn(&run_exe_unit_tests.step); 37 | } 38 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "req", 3 | .version = "0.0.0", 4 | .minimum_zig_version = "0.12.0-dev.3318+6724a524d", 5 | 6 | .dependencies = .{}, 7 | 8 | .paths = .{ 9 | "", 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const http = std.http; 3 | const heap = std.heap; 4 | 5 | const Client = http.Client; 6 | const RequestOptions = Client.RequestOptions; 7 | 8 | const Todo = struct { 9 | userId: usize, 10 | id: usize, 11 | title: []const u8, 12 | completed: bool, 13 | }; 14 | 15 | const Post = struct { 16 | userId: usize, 17 | body: []const u8, 18 | title: []const u8, 19 | }; 20 | 21 | const FetchReq = struct { 22 | const Self = @This(); 23 | const Allocator = std.mem.Allocator; 24 | 25 | allocator: Allocator, 26 | client: std.http.Client, 27 | body: std.ArrayList(u8), 28 | 29 | pub fn init(allocator: Allocator) Self { 30 | const c = Client{ .allocator = allocator }; 31 | return Self{ 32 | .allocator = allocator, 33 | .client = c, 34 | .body = std.ArrayList(u8).init(allocator), 35 | }; 36 | } 37 | 38 | pub fn deinit(self: *Self) void { 39 | self.client.deinit(); 40 | self.body.deinit(); 41 | } 42 | 43 | /// Blocking 44 | pub fn get(self: *Self, url: []const u8, headers: []http.Header) !Client.FetchResult { 45 | const fetch_options = Client.FetchOptions{ 46 | .location = Client.FetchOptions.Location{ 47 | .url = url, 48 | }, 49 | .extra_headers = headers, 50 | .response_storage = .{ .dynamic = &self.body }, 51 | }; 52 | 53 | const res = try self.client.fetch(fetch_options); 54 | return res; 55 | } 56 | 57 | /// Blocking 58 | pub fn post(self: *Self, url: []const u8, body: []const u8, headers: []http.Header) !Client.FetchResult { 59 | const fetch_options = Client.FetchOptions{ 60 | .location = Client.FetchOptions.Location{ 61 | .url = url, 62 | }, 63 | .extra_headers = headers, 64 | .method = .POST, 65 | .payload = body, 66 | .response_storage = .{ .dynamic = &self.body }, 67 | }; 68 | 69 | const res = try self.client.fetch(fetch_options); 70 | return res; 71 | } 72 | }; 73 | 74 | pub fn main() !void { 75 | var gpa_impl = heap.GeneralPurposeAllocator(.{}){}; 76 | defer if (gpa_impl.deinit() == .leak) { 77 | std.log.warn("Has leaked\n", .{}); 78 | }; 79 | const gpa = gpa_impl.allocator(); 80 | 81 | var req = FetchReq.init(gpa); 82 | defer req.deinit(); 83 | 84 | // GET request 85 | { 86 | const get_url = "https://jsonplaceholder.typicode.com/todos/1"; 87 | 88 | const res = try req.get(get_url, &.{}); 89 | const body = try req.body.toOwnedSlice(); 90 | defer req.allocator.free(body); 91 | 92 | if (res.status != .ok) { 93 | std.log.err("GET request failed - {s}\n", .{body}); 94 | std.os.exit(1); 95 | } 96 | 97 | const parsed = try std.json.parseFromSlice(Todo, gpa, body, .{}); 98 | defer parsed.deinit(); 99 | 100 | const todo = Todo{ 101 | .userId = parsed.value.userId, 102 | .id = parsed.value.id, 103 | .title = parsed.value.title, 104 | .completed = parsed.value.completed, 105 | }; 106 | 107 | std.debug.print( 108 | \\ GET response body struct - 109 | \\ user ID - {d} 110 | \\ id {d} 111 | \\ title {s} 112 | \\ completed {} 113 | \\ 114 | , .{ todo.userId, todo.id, todo.title, todo.completed }); 115 | } 116 | 117 | // POST request 118 | { 119 | const post_url = "https://jsonplaceholder.typicode.com/posts"; 120 | const new_post = Post{ 121 | .title = "Simple fetch requests with Zig", 122 | .body = "Make sure to like and subscribe ;)", 123 | .userId = 1, 124 | }; 125 | 126 | const json_post = try std.json.stringifyAlloc(gpa, new_post, .{}); 127 | defer gpa.free(json_post); 128 | 129 | var headers = [_]http.Header{.{ .name = "content-type", .value = "application/json" }}; 130 | const res = try req.post(post_url, json_post, &headers); 131 | const body = try req.body.toOwnedSlice(); 132 | defer req.allocator.free(body); 133 | 134 | if (res.status != .created) { 135 | std.log.err("POST request failed - {?s}\n", .{body}); 136 | std.os.exit(1); 137 | } 138 | 139 | std.debug.print("POST response body - {s}\n", .{body}); 140 | } 141 | } 142 | --------------------------------------------------------------------------------