Just looking for a small review into this piece of code that sends a get request to some API. Is this how it would/should be done or is there a better/more efficient way?
One nitpick I have for your code example is the global allocator. It’s not that global allocators can’t exist (they certainly can), but I personally prefer to move things like that into the scope they’re being also being deinit’d from (if possible - in this example it certainly is). Personally, I’d move that to main and then have the deinit statement directly below that - keeps everything in the same scope and makes it (in my opinion) more readable.
One more addendum here. I’d like to point you towards the GPA source code file - you’ll see this in there:
/// Returns `Check.leak` if there were leaks; `Check.ok` otherwise.
pub fn deinit(self: *Self) Check {
const leaks = if (config.safety) self.detectLeaks() else false;
if (config.retain_metadata) {
self.freeRetainedMetadata();
}
self.large_allocations.deinit(self.backing_allocator);
self.bucket_node_pool.deinit();
self.* = undefined;
return @as(Check, @enumFromInt(@intFromBool(leaks)));
}
You’ll see that the GPA allocator has it’s own deinit function (it contains state). When you call deinit on the GPA, it will return a value about the attempted deinit call. Here’s the values it can return:
pub const Check = enum { ok, leak };
You can check these values like so:
// needs to be against the gpa_impl - not the allocator interface by calling "allocator()".
if (gpa_impl.deinit() == .leak) // do something (probably call @panic)
It’s good to get into the habit of checking if the GPA tells you that you are leaking memory and you can use that return value to do so
although, de-initialising the allocator and checking it that way seems like the better way of doing so, thanks for that!
Apart from my allocator use, is there any feedback on the way of sending the request? I guess in some sense I’m attempting to naively implement something like fetch() from Javascript world.
Yes, I did see that std.http.Client had a fetch() method, although I wasn’t too sure what the difference between that and open() were and which would be better when making a request to some api.
Your code looks about right for how to use the HTTP client. Right now I use this blog post as a reference for a basic client/server model.
As for how you are doing the request, fetch request are all asynchronous by default, whereas it looks like you are sending a blocking request. Also if you really want to implement a way to handle simple fetch requests at this level, then you are missing some things like declaring the MIME type in the headers, also handling different request methods (POST, PUT, etc…). And you also will want some way to handle CORS requests. This MDN article has a good example for making a POST fetch request in JavaScript, under the section “Supplying request options”. i.e. Request method, request mode (cors/non-cors), cache control, supplying credentials, mime type, as well as the redirect and referrer policies. Though I’m not exactly sure how I would implement all of that in Zig, it gives you a good idea of what a fetch request should be able to do.
For making requests to an API then you want to go with a fetch request. I haven’t tried out the Zig implementation (Client.fetch()), but if it’s there then that’s going to be the way to go. API requests are typically made as an AJAX request (async with no page reload) in the browser with JavaScript using XMlHttpRequest (legacy method) or fetch(). Whereas Client.open() is much more broad in nature, and would be more like just making a regular URL request in your browser bar.