Writing an API Wrapper in C: The API Client

Welcome to the first blog in a series I will be calling Writing an API Wrapper in C. I’ll be talking about the API client in the first article.

API wrappers are extremely common as interfaces for data have moved from system libraries to the web. Libraries buy you first class language interaction with your data while web interfaces generally lack type checking like a compiler or interpreter would provide.

Enter API wrappers.

API wrappers represent first-class language contracts for interacting with data or services over the web. They allow you to use the niceties of your language of choice using an abstraction over a web interface.

API wrappers typically come in much higher-level languages like Go, Python, JavaScript, Java, and C#, but in this blog I will show you how to make an API wrapper in C. For the purpose of the series, I will be borrowing code from my project harvest-glib which I make use of in my other project Harvest Almanac . We will be taking a look at the Harvest API. The docs can be found here: Harvest API Documentation . Harvest is a service for tracking time and expenses on projects. We use it at work often.

Technologies I will be showcasing within the series include:

  • GLib - Expanded standard library for C.
  • GObject - Object-oriented type system for C. I could spend forever talking about this alone, but it wouldn’t really stay on topic. If you see anything confusing, email me or leave a comment.
  • libsoup - GLib-based HTTP library.
  • json-glib - JSON support for GLib.

You might be saying, “Tristan, but C lacks all the niceties that higher-level languages have so this feels like a waste of time.” And to that I say time is only wasted if you don’t see value. Let’s call this an exercise of the mind and dive right into the first topic in the series: Creating an API Client.

HarvestApiClient

An API client is the object that will execute requests, and format responses into language constructs like objects and errors. Generally APIs will require some form of authentication along with any special headers that may need to be set. The Harvest API requires authentication, so I generated a developer token. It also requires a few other parameters, like an account ID, and a user agent including a contact email. Here is how I am creating a Harvest API client in C.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// harvest-api-client.h
HarvestApiClient *harvest_api_client_new(SoupSession *session, const char *access_token,
  const char *account_id) G_GNUC_WARN_UNUSED_RESULT;

// my-code.c
g_autoptr(SoupSession) session = soup_session_new_with_options(SOUP_SESSION_MAX_CONNS,
  4, SOUP_SESSION_USER_AGENT, "HarvestAlmanac (me@example.com)", SOUP_SESSION_ADD_FEATURE_BY_TYPE,
  SOUP_TYPE_CONTENT_SNIFFER, NULL);

// CLIENT is a singleton that will handle all my Harvest API interactions
HarvestApiClient *CLIENT = harvest_api_client_new(session, access_token, account_id);

It would probably help to see a little bit of the definition of HarvestApiClient.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// harvest-api-client.c
struct _HarvestApiClient
{
  GObject parent_instance;

  SoupSession *session;
  char *server; // Base URL of the API, in this case always "https://api.harvestapp.com/v2"
  char *access_token;
  char *account_id;
};

These struct members and the request object I am given are all that I need to successfully make a request to the Harvest API. The API client has two public functions to be aware of.

1
2
3
4
// harvest-api-client.h
HarvestResponse *harvest_api_client_execute_request_sync(
  HarvestApiClient *self, HarvestRequest *req);
void harvest_api_client_execute_request_async(HarvestApiClient *self, HarvestRequest *req);

You see the types HarvestRequest and HarvestResponse in the code. We will ignore that for now since it will be talked about later in the series. Just know HarvestRequest includes the body, the HTTP method, the API endpoint, and metadata about what constitutes a successful response, while HarvestResponse includes the deserialized response object, the error if there was one, and the received status code.

We are good developers so we provide both an asynchronous and synchronous way to use our client. Benefits of the synchronous API include blocking calls, while the asynchronous API can benefit graphical interfaces or event loop style programs where you react to events. The implementations of the execution functions will be covered later when we talk about HarvestRequest and HarvestResponse because they go hand in hand.

Hopefully this blog post was enough to trigger your interest in the series as there is more content to come.

Have a comment or question on one of my posts? Start a discussion in my public inbox by sending an email to ~tristan957/public-inbox@lists.sr.ht. If you are unfamiliar with mailing lists, start with mailing list etiquette. Otherwise just shoot me an email to my address which is linked in the sidebar. If you have a GitHub account and JavaScript enabled, then this site also has utteranc.es support for comments.

Articles from blogs I follow

Launching the 2020 State of Rust Survey

It's that time again! Time for us to take a look at how the Rust project is doing, and what we should plan for the future. The Rust Community Team is pleased to announce our 2020 State of Rust Survey! Whether or not you use Rust today, we want to know…

via Rust Blog September 10, 2020

Mozilla CEO Mitchell Baker urges European Commission to seize ‘once-in-a-generation’ opportunity

Today, Mozilla CEO Mitchell Baker published an open letter to European Commission President Ursula von der Leyen, urging her to seize a ‘once-in-a-generation’ opportunity to build a better internet through … Read more The post Mozilla CEO Mitchell Baker ur…

via The Mozilla Blog September 7, 2020

Linux development is distributed - profoundly so

The standard introduction to git starts with an explanation of what it means to use a “distributed” version control system. It’s pointed out that every developer has a complete local copy of the repository and can work independently and offline, often contra…

via Blogs on Drew DeVault's blog September 2, 2020

Generated by openring