Tradeoffs in API Design
There are a few choices when designing an API layer: REST or RPC, binary or plaintext, TCP or HTTP, schema or schemaless? A few of the tradeoffs and an overview of some of the tools.
Schema-driven? You can do this with REST (e.g., OpenAPI/Swagger) and RPC (e.g., protobufs). While having a schema means you can automatically scaffold client/server stubs and enforce message types, it doesn't come for free. The code generation step adds friction to the developer workflow, and developing outside the happy path is extra painful – let's say you have to modify a client/server stub; how do you incorporate that into code generation?
Plaintext vs. Binary? Plaintext protocols are simple to implement, human-readable, and easier to debug. Yet, binary formats are significantly faster (often by an order of magnitude). In addition, some things are much more difficult in plaintext, like streaming.
Compatibility? gRPC is notoriously hard to get working in the browser since it relies on HTTP/2 and things like HTTP trailers. But if you're streaming large blobs between app-to-app, it might be significantly more straightforward than using something like WebSockets or plain REST. On the other hand, if you're building an API product for consumers, you might want to use something more widely compatible, like REST or JSON-RPC.
RPC vs. REST? For many create-read-update-delete (CRUD) applications, REST is an excellent choice because it's resource-based.
GraphQL? It solves many organizational problems between teams that would have had to otherwise rely on fine-grained REST APIs. It decouples the data model from the API. Yet, that requires a lot of thought from the backend to optimize queries and rate limit clients.