Choosing the Right API Technology
These are fantastic times for (web) developers: there is a plethora of API technologies one can use, offering distinct trade-offs. Which begs the question:
What API technology should I choose?
The following is my (current) take on when to use REST(-like) APIs, RPC frameworks, GraphQL, or SOAP.
In RESTful - or the more common REST-like - APIs, very roughly, requests identify a resource to retrieve or mutate.
I think of them as the workhorses of APIs. They have proven their worth and they get the job done in many cases. They also feel familiar to most developers: knowledge of the semantics of HTTP methods or status codes is fairly commonplace, even if improper usages exist.
In some sense, REST(-like) APIs simplify the job of backend developers who tightly control resources and possible interactions with them. Mechanisms like access control, rate-limiting, or caching are well-established, even for exposing APIs publicly.
The applicability of REST-like APIs has its limits, though. Maintenance burdens, performance considerations, or lacking developer experience (DX) have led to the creation of GraphQL at Facebook (Meta), Falcor at Netflix, or gRPC at Google. Often, these technologies emerged due to the specific requirements found in organizations of extreme scale (in terms of numbers of engineers/teams/API clients, system size, request volume etc.). In many instances, though, a REST-like API might just be fine.
Use REST-like APIs if:
- You want to expose an API publicly...
- ...and benefit from a mature ecosystem of tooling and services (think API gateways, caching, description and documentation formats etc.)
- Ideally, you expect your API to be relatively stable and your endpoints to fit the vast majority of client requirements well
In Remote Procedure Call frameworks, requests identify a procedure, which is invoked with sent parameters, and produces response data.
Maybe counter-intuitively, the tight coupling induced by RPC frameworks is a strength. Consider, for example, how tRPC's static type-checking points developers to places in the frontend code that need to adapt after changing the backend (and that without having to compile the backend!). In addition to offering great DX, RPC frameworks can benefit performance, for example when relying on binary data formats like Protocol Buffers or when batching requests. However, RPC frameworks are often specific to certain programming languages or even application frameworks, thus limiting their applicability. I think of them as trading off (broadly) flexibility for performance and/or DX.
Use RPC frameworks if:
- You desire the DX and/or performance benefits offered by RPC frameworks...
- ...and are fine with tightly coupling backend and client/frontend code (i.e., you assume that the set of required API operations is fixed; backend and client/frontend code is co-located and maintained by the same team)
GraphQL APIs enable clients to define exactly what data to retrieve or mutate using queries.
Taming the flexibility that GraphQL thus grants to clients is a well-known challenge. At IBM Research, we looked into ways to detect and constrain arbitrary GraphQL queries at runtime. More recently, persisted queries have gained popularity: during development, developers use GraphQL's full flexibility for defining queries. At runtime, clients switch to invoking "persisted" versions of these queries (typically identified by some id). This pattern allows API providers to validate and vet queries only once and optimize their execution. It also removes the need of clients to send (large) queries in every request, and it eases caching of requests.
A recent discussion proposes that using GraphQL with persisted queries is basically the same as building a REST-like or RPC API. Like the author of the linked article, I don’t agree. GraphQL helps to decouple the evolution of backends and clients in light of diverse, changing client requirements. REST and RPC do not. Using the latter two, if the need for such decoupling arises, there are basically two options: a) add more specialized resources or procedures, which eventually becomes to maintenance burden, or b) overload resources or procedures to behave differently given different parameters - at which point you are basically reimplementing GraphQL.
That said, I have observed uses of GraphQL that do not convince me. I.e., using it for internal APIs in small organizations and with well-contained numbers of clients. In such cases, I find RPC frameworks simpler to use.
Use GraphQL if:
- You want to reduce coupling between backend teams and (numerous, diverse) frontend/client teams (implying: these teams exist and are actually separate, be it within your organization or externally)
- You want clients to have a maximum of flexibility in consuming the data they require, without putting an unreasonable maintenance burden on backend teams
- You want clients to have a great DX, even if they aren't part of your organization (think API exploration, static typing etc.)
Use SOAP if:
- You require very specific semantics at the message level (like transactions, or reliable delivery) and...
- ...you work at a large organization with lots of legacy code and the decision to migrate is above your pay-grade, unfortunately 👿
At Coup, we persist data in Firestore, which our client-side web application interacts with directly. Some "backend" logic resides in Cloud Functions that are asynchronously triggered by data mutations. As such, we don't have any internal APIs, and we also don't (yet) expose a public API. However, if we weren't using this approach, I'd otherwise opt for using a RPC framework like tRPC for internal APIs, given the small size of our development team and as we don't separate between frontend and backend engineers. For building a public API, I'd currently lean towards a REST-like API. Our domain model is straight-forward and I am attracted by the simplicity that comes when tightly controlling what requests are possible, especially for external parties.
Not going with GraphQL might be surprising given my past involvement in that technology. But at Coup we currently don't face the challenges GraphQL solves, IMHO 🤷♂️.