Will's avatar

⬅️ See more posts

The simplicity and flexibility of HTTP for APIs

31 March 2021 (6 minute read)

🔮 This post is also available via Gemini.

100daystooffload technology opinion

💯 100 Days to Offload

This article is one of a series of posts I have written for the 100 Days to Offload challenge. Disclaimer: The challenge focuses on writing frequency rather than quality, and so posts may not always be fully planned out!

View other posts in this series.

Simple and RESTful HTTP APIs

The HTTP standard is an expressive system for network-based computer-computer interaction. It’s a relatively old standard - it started life as HTTP/1.0 in 1996 and the HTTP/1.1 standard was formally specified in 1999. HTTP/2 (2015) introduced efficiencies around how the data is transmitted between computers, and the still in-draft HTTP/3 builds further on these concepts.

I won’t go into the nuts and bolts of it, but - essentially - for most applications and APIs, the developer-facing concepts haven’t really changed since HTTP/1.1. By this version, we had all the useful methods required to build powerful and flexible APIs.

When writing a web service (e.g. a website or a web-based REST API), actions are based around resources. These are the “things” or “concepts” we are concerned with. For example, if one was to write a to-do list app, two of the concepts might be “to-do list” and “to-do list item”. Generally, such an app might also maintain user accounts and so may have “user” and “session” resources, too, along with others if required.

In such a service, resources are usually indicated by a path. This is the bit that comes after the host name (e.g. example.com) in the URL and are usually mentioned in plural.

For example, in our to-do list example, a resource which indicates all available lists might be given simply by /lists, and a specific list with ID aaa-bbb-ccc would be available at /lists/aaa-bbb-ccc.

This system allows the engineer to indicate hierarchy or ownership in the data model. For example, to address all of the list items in a specific list one might use /lists/aaa-bbb-ccc/items. Then to access an item with ID xxx-yyy-zzz inside this list you’d use /lists/aaa-bbb-ccc/items/xxx-yyy-zzz. In many cases, for a simple web service of this type, this would be sufficient - it may not be appropriate to enable addressing a to-do list item directly without the context of its “parent” list.

Paths may sometimes include other metadata, such as the API version being called, but this can be simply included and described in the documentation.

In HTTP, methods describe what the API consumer wants to do with a resource. Some of the most widely used methods are GET, POST, PUT, DELETE, and OPTIONS. These methods are defined in the spec and some clients may handle requests differently based on the method being used. Unlike resources, you can’t define your own methods to use. However, the flexibility provided as-is allows for most services to be built without requiring this level of customisation.

Some of these have pretty obvious semantic meaning. POST is typically used to create a new resource (e.g. “post a new to-do list item”) and PUT is used to update an existing resource.

This means that, given the combination of our resource addressing and methods, we can express a powerful web service. Structuring your to-do list app using the system described here caters well to typical to-do list actions: creating lists and items (e.g. POST /lists/aaa-bbb-ccc/items), crossing-off items (probably PUT /lists/aaa-bbb-ccc/xxx-yyy-zzz), and retrieving, updating, and deleting things in a similar way using the appropriate methods.

HTTP request headers can be used to provide authentication information, describe how the client wants information to be returned in the response, along with other ways to further annotate the request being made and to customise the expected response. Of course, the effectiveness of supplying these request headers depends on the server’s own capability and configuration. However, the use of headers should certainly be considered by the engineer whilst planning and building out the service.

Using standards like these - resources, methods, and headers - in your APIs enables your users (consumers) to more easily learn and understand how to use your service. This saves them time, helps your service to grow, and means you’ll spend less time dealing with support requests (unless your documentation is really good).

Custom implementations

I think the above system is the most ideal, expressive, learnable and expected way of building web services.

However, HTTP is flexible, and your server-side code can - in theory - do whatever you want it to, no matter what the request path, method, and headers are. But I don’t really understand why one would want to.

I recently migrated my photos collection to pCloud, and wanted to explore their API to see if I could also use the service for programmatically backing-up other things, too.

Unfortunately I am unable to actually use their API, since I use two-factor authentication on pCloud and the API doesn’t seem to work if this extra layer of security is in-place. However, whilst researching I discovered that pCloud’s API is an example of a service that seems to defy the standards one is usually familiar with.

For example, it appears that it’s perfectly acceptable to use POST https://api.pcloud.com/deletefile?fileid=myfile to delete a file or GET https://api.pcloud.com/renamefolder?path=oldfolder&topath=newfolder to rename a folder.

There’s nothing technically wrong with this implementation, especially given the fact that I’m sure it works. It perhaps makes it easier to route requests through to the correct internal functions. However it just feels inelegant to me, and it seems to focus more on what’s easier for them rather than their users.

The page that lists file operations could instead show a couple of simple example paths and then rely on request methods and parameters to describe available options.

I don’t mean to pick on pCloud - the service itself is great and I’m sure the API works nicely. I plan to continue using the service via its web UI and official clients. I only bring it up because it seems odd to re-invent the wheel.

I’m completely on-board with the notion of discouraging system and process monopoly, but I don’t think this is the same thing. The web is formed from a set of open standards that anyone can comment on or help contribute to.

“Good” implementation examples

The web is full of services that expose sensible and learnable APIs.

An example I always love is the Stripe API - arguably a much more complex service than pCloud. However its simple “compliant” API makes credit card payments - and loads more - very easy to integrate with.

The Spotify web API also looks useful, though I haven’t used that before myself.

Beyond REST

REST has been a cornerstone of the web over the past couple of decades, and I think there is still very much a space for it - both for now and in the near future. Its flexibility has allowed it to remain useful across industries and settings - from small private IoT setups through to highly-secure enterprise-enterprise systems.

There are movements to begin using other technologies that may be better suited to the future of the web and communication - particularly as things continue to scale. Efforts such as GraphQL, Netflix’s Falcor project, and even RPC provide alternatives for when REST isn’t the most appropriate solution.

However, if building a web API that you want other people to use, and which would be well suited to REST, then I always think it’s worth sticking to these HTTP standards as much as possible.

✉️ You can reply to this post via email.

📲 Subscribe to updates

If you would like to read more posts like this, then you can subscribe via RSS.