25 April 2018
What makes a mobile-friendly REST API?
REST allows for considerable variation in terms of end-point design and implementation detail. Many design nuances are dependent on the clients that will be consuming the resources. APIs that are designed for server-based data integrations tend to look quite different from those that are designed to support mobile applications.
Mobile applications do face a particular set of challenges that should influence API design. Networks are always slow. Devices are even slower. Connections are unpredictable and users expect to be able to swap between devices seamlessly. Requirements are prone to sudden change. Developers tend to be in a hurry.
Added this is the fact that APIs and mobile applications are often built and managed by separate teams. This can give rise to numerous integration problems if the APIs are not explicitly targeted for mobile applications.
Consistency and convention
API usability is about predictability as much as anything else. One of the advantages of REST is that it provides a set of sensible and widely understood defaults for API behaviour. You should use them.
It’s easy to get consumed in unhelpful debates about what is and is not “RESTful”, but there are some basic expectations here. You should use GET to fetch data, DELETE to remove it, POST to create new resources and PUT for idempotent updates. Support content negotiation to let the client choose the content type. Use proper HTTP status codes so the client can tell when something has gone wrong (or right).
That said, consistency across an API is probably more important than conforming to widely-held convention. There’s nothing worse than an unpredictable API that implements different behaviours across different resources. If you’re going to be wrong, then at least be consistently wrong.
Division of labour
Any API design requires decisions around where processing should take place. You do not want your API to burden mobile devices with expensive data operations. You might also have several different client applications in development, each requiring similar functionality. Ideally, all a mobile application should have to do is render output and accept input – the server should do all the heavy lifting where possible.
There are nuances to observe here, of course. Over-reliance on a server for basic functionality can render an app useless if there’s no network coverage. Server-based processing can also give rise to an overly “chatty” relationship between application and API which can undermine performance.
API calls are expensive. You should try and minimise the number that any application needs to make. A client should not have to make multiple, recursive calls to get the data it needs. It should also be able to pass updates in a single batched transaction rather than as a set of separate updates.
From a strict performance point of view the ideal is to populate each screen with a single call and allow data updates to be passed with a single call. There is a trade-off here, as coupling the API design to the application UX may leave it unusable for any other type of client.
Managing change, particularly breaking change, is an acute issue for mobile applications as you will inevitably have numerous versions of clients in use and little direct control over when they are upgraded.
API versioning can be a minefield. If you adopt a strict versioning strategy you will have to maintain multiple API versions in production. Anticipating future changes by adding optional parameters and wildcard properties tends to give rise to a confusing and messy design. Likewise, for an evolutionary approach where explicit versioning is rejected in favour of augmenting but never changing APIs.
The challenge here is to manage inevitable change in an API whilst providing a stable contract to clients. This is a two-way street as your API consumers also have responsibilities here. Ideally, they should seek to be tolerant readers that only consume the parts of any response that they really need. Consumer-driven contracts can help to clarify the expectations that consumers have of their APIs so developers are more aware of braking change.
Implementing HATEOAS can reduce the scope of breaking change by giving the server control over feature availability and de-coupling clients from the format of URIs. This does rather rely on clients to do their part and use the API “properly” by following links provided in responses. The catch with HATEOAS is that the “correct” style of client integration is purely voluntary and there’s no way to enforce it.
Mobile API development tends to be at the mercy of numerous UX-driven interface tweaks that require corresponding changes to data. It’s easy to get locked into a repeating bottleneck where small feature requests get bounced back and forth between mobile and API teams.
Ideally an API should offer a degree of flexibility, allowing clients to control the shape of a response via query string arguments. There are query formats such as OData, GraphQL and ORDS that provide a standard for this kind of flexibility. They allow clients to filter data rows, implement paging or modify ordering without requiring any code changes on the API.
These kind of query formats do help to support more flexibility in UI design, though this often comes at the cost of increased complexity both of the technical implementation and API design. These formats also tend to be opinionated about the underlying API architecture. OData in particular couples underlying data stores to public-facing end-points in a way that is not to everybody’s taste.
These query formats, though convenient, also have the potential to weaponise API requests. In exposing a flexible API you may be writing cheques that you cannot really cash if consumers start hitting you with expensive custom queries.
That said, you should try to meet your clients half way by at least providing some flexibility over the format of responses. At the very least, allowing for pagination, ordering and basic filtering through query string arguments can give clients a lot more room to make UI adjustments without requiring corresponding code changes in APIs.
No matter how intuitive or RESTful your API may be, it will need relevant and accurate documentation to be at all useful. Alas, this is often left to the last minute and executed by hurried development teams who are not accustomed to producing consistent and accurate documentation.
Tools such as Swagger and API Blueprint can help to provide some level of automated documentation. They may have their detractors (including me) but they can at least reflect whatever is in production.
Automated documentation can provide a basic description of end-points, but it can never explain context such as explaining features or advising on best practice. This richer information is just as important as lists of resources and it can only really be maintained manually, preferably by a technical writer.
Considering offline operation
You cannot assume that a single device will have a consistent and unbroken connection to an API. At the very least you will need to consider the effects of offline activity and how you might reconcile any data that it generates.
You may also want to ensure fluid operation across multiple devices as users switch between mobile, tablet and PC. They will expect a seamless experience with data being shared freely and securely between different clients. This is more than just an application concern. For example, an API may need to implement a system of timestamps to marshal concurrent updates sent from multiple devices.
Use SSL all the time, even in development. It makes life easier in the long term as you won’t be catching encryption issues as you switch between environments. There’s really no excuse for not using signed certificates, particularly since the advent of Let’s Encrypt.
When something goes wrong you need to provide meaningful information to a client can respond appropriately. This means making use of appropriate HTTP status codes for common conditions such as poorly formatted requests (400 Bad Request) and referring to records that do not exist (404 Not Found).
It should also mean providing helpful responses for unexpected errors, so a client can respond appropriately. Verbose error messages tend not to be much use for a mobile application, other than to confuse the user with technical detail. A better strategy is to log detailed error conditions on the server and return error codes that an application will be able to respond to sensibly.
Mobile APIs need to be fast. Anything you can do to push data closer to client applications or reduce the amount of data being processed is worth considering. Caching static resources in a CDN can help to make an API more responsive, or if that isn’t possible your API could leverage a fast memory-based data store to speed up responses for more common requests.
HTTP also provides a few different strategies for response caching, such as using ETags, the If-Modified-Since header or the Cache-Control header. All these options help to reduce the amount of data being transferred, though the most appropriate option will largely depend on your data and how it has been persisted.
HTTP compression is supported by many clients, so it makes sense to enable it on servers. Anything you can gain in terms of bandwidth usage is worth having, particularly when it is so easy to implement.