Zurl - HTTP Request tool written in Zig

Posted on Apr 24, 2024

Zurl is a command-line tool written in Zig, designed to provide a simplified interface to perform HTTP requests, inspired by the functionality of curl.

Motivation

When building/testing APIs I often find myself executing a lot of api calls, multiple times, with different parameters, managing authentication manually and a lot of other funny things.

Usually I use curl and postman but I find the last one faster due the fact that I can save the requests, execute them with just one click, managing enviroments variables (things such as host, apikey …) and use scripts to automate the authentication via pre-request scripts (there is a lot of dark magic you can achieve with pre-request script).

But at the same time I just want to stay in my terminal with my hands on the keyboard.

I don’t know if a tools that meets these requirements already exists, or if I can achieve the same result using curl (I did a small research with no results), but I’ve decided that this project could be really nice to implement in Zig.

Libraries

At the moment I’m using 3 libraries in the project

  1. Clap - An amazing library for command line argument parsing.
  2. libcurl - I’m relying on libcurl for http requests. There are two main reason why I’m using libcurl. It’s an amazing library and I want to test/learn interoperability between Zig and C.
  3. sqlite3 - I’m using sqlite3 as storage to save requests.

Let’s Go

The first thing I did is to use devbox to create a reproducible shell with all that is needed to build the project. I lost hours trying to understand how to add headers and libraries of a package (you just need to add ^dev). From now on (on every pc) I just to type devbox shell, to get Zig, zls (language server) and all the tools needed to build Zurl.

One of the best feature of Zig is the build.zig file (goodbye Makefile), where you can specify your build graph. I just linked libC, libcurl, sqlite3 and clap (7 lines of code), and everything works smooth. I can just import libcurl and sqlite3 using

@cImport(@cInclude("curl/curl.h")); 
const sqlite = @cImport(@cInclude("sqlite3.h"));

The interoperability with C that Zig offers is really amazing. I just have some troubles using C NULL but turns out I just need to define it

const NULL: ?*anyopaque = null;

Features

At the moment zurl can execute (GET,POST,OPTIONS,PATCH,PUT) http request, save/load them using sqlite database. You can specify headers, query params and json data.

The command below will execute the GET request and save it with the name pokemon/mewtwo. You can the re-execute the request simply loading from the storage.

zurl --save pokemon/mewtwo --db zurl.db --method GET https://pokeapi.co/api/v2/pokemon/mewtwo
zurl --find pokemon/mewtwo --db zurl.db

What’s next?

I’m not really happy about the command arguments, so I will start to refactor them.

Then I will work on the most important features to me:

  1. Specify which kind of information you want back after executing the request (something like -i in curl)
  2. Use of environment variables (zurl –env API_KEY=123 –header api-key={API_KEY} –method GET https://pokeapi.co/api/v2/pokemon/mewtwo)
  3. Pre-request scripts

In the end

Few days ago Zig 0.12 was realeased, so I will try to update from Zig 0.11 when the new version will be available on Nix.

In the end I will try to focus on tests, but just for fun. At the moment I don’t have any clear idea on how to approach this stage.