REST versus RPCs
How are they different and how do we choose?
Representational State Transfer (REST) and Remote Procedure Calls (RPCs) are both API paradigms used for building networked applications, but they differ significantly in design principles, protocol behavior, and intended use cases.
REST emphasizes statelessness, resource-oriented design, and standard HTTP methods, giving it potential for interoperability, scalability, and simplicity in distributed applications.
Conversely, RPCs, such as gRPC, focus on function-oriented interactions, giving it potential for lower-latency communication and explicit typing, which can be useful in tightly coupled or high-performance systems.
REST can become verbose for complex operations, while RPCs may suffer from reduced interoperability and higher coupling. Choosing between them involves evaluating factors like system requirements, client diversity, performance needs, and long-term maintainability — as well as understanding how to mitigate their risks.
To help you evaluate the tradeoffs, here’s a detailed comparison from multiple perspectives:
Core Concept and Philosophy
RPC (Remote Procedure Call): RPCs allow a client to directly invoke procedures or methods on a server as if they were local functions. They emphasize actions (calling functions) rather than the resources on which those actions operate. RPCs are typically procedural and function-oriented.
REST (Representational State Transfer): REST, first articulated by Roy Fielding, is a set of architectural constraints for managing networked resources. REST treats the server as a collection of resources that can be manipulated through standard HTTP methods (like GET, POST, PUT, DELETE). REST is resource-oriented rather than action-oriented.
Communication and Protocols
RPC: Communication in RPC-based services can occur over various protocols, including HTTP, TCP, or custom binary protocols. Common RPC implementations are:
- gRPC also known as Google Remote Procedure Call is an open-source remote procedure call system initially developed at Google. It uses HTTP/2 for transport and uses Protocol Buffers as the interface description language.
- GraphQL is an open-source data query and data manipulation language for APIs, and a runtime for fulfilling queries from persistent data. It allows clients to define the structure of the data requested — with that data structure being returned from the server.
REST: RESTful communications are usually (but not required to be) HTTP-based. Each resource has a unique URI, and HTTP methods (GET, POST, PUT, DELETE) determine the interaction type. REST emphasizes using HTTP as both the transport and application protocol.
Statefulness and Idempotency
RPC: RPCs often do not enforce statelessness — the server may maintain session-based states or server-side data. Some RPC calls, like those in gRPC, can have long-lived sessions or bidirectional streams, which allow for more stateful interactions.
REST: RESTful services are designed to be stateless, meaning each request from the client to the server contains all the necessary information to process it. REST emphasizes idempotency (repeating an operation produces the same result), especially with GET and DELETE methods, which makes REST APIs easier to scale horizontally.
When creating adaptable, composable applications, statelessness means services can be reconfigured, replaced, or scaled independently. If requests are self-contained, services can be bound and unbound on the fly, enabling the dynamic composition of services without worrying about dependency on server states.
Data Format and Serialization
RPC: Many RPC protocols allow for a flexible choice of serialization formats. gRPC, for example, uses Protocol Buffers (binary serialization) for efficient data exchange, which is faster and creates smaller data interchange packages than JSON.
REST: REST APIs traditionally use JSON over HTTP for data interchange because it is both human-readable and lightweight. REST’s reliance on text-based formats (JSON) can lead to increased payload sizes and slower performance compared to binary formats. Zip compression is built into HTTP clients and servers and, if used, helps to mitigate that overhead.
Client-Server Interaction Model
RPC: RPC interactions closely resemble method/function calls in programming, where the client makes a request and awaits a response. The procedure and parameters are tightly coupled, so the client often needs to know specific method signatures to call on the server. RPCs generally require a predefined interface or contract, which the client and server agree on beforehand.
REST: RESTful interactions are resource-centric, and clients interact with resources through a limited set of actions (GET, POST, etc.). The client generally needs to know about the resource types but not the specific functions, making REST more flexible. A client can work with a REST API without needing to understand the exact implementation of the service.
Performance and Efficiency
RPC: RPCs, especially gRPC, can be more efficient for high-performance scenarios due to their use of binary data formats and multiplexed streams over HTTP/2. RPCs’ procedural nature can result in lower latency for certain operations, especially in microservices and internal APIs. Binary formats, like Protocol Buffers (Protobuf), require the added complexity of distributed schema management.
REST: REST over HTTP/1.1 is generally less performant than binary-based RPC protocols, as JSON payloads and stateless interactions can add overhead. However, REST can benefit from HTTP caching and Zip compression, which can enhance the performance of REST APIs.
Flexibility and Scalability
RPC: RPCs can be restrictive as they require a rigid definition of methods and procedures. Changes to the server’s API (like adding or modifying methods) often require corresponding updates to clients. This coupling can limit flexibility, especially when evolving services.
REST: REST is highly flexible because it decouples client and server interactions, allowing the server’s internal logic to change without impacting the client, as long as resource representations stay consistent. REST’s statelessness also supports horizontal scaling, as each request is independent and can be handled by any instance of the service.
Error Handling and Responses
RPC: RPC error handling often relies on exceptions or status codes defined by the protocol (that is, gRPC has defined error codes for various conditions). Since RPCs closely resemble function calls, errors are typically thrown or returned directly to the client in a structured way.
REST: REST typically uses HTTP status codes for error handling, which map well to the behavior expected from REST clients. REST responses include metadata, such as headers, which can convey more information about errors and responses.
Security
RPC: RPC security depends on the protocol. gRPC, for example, uses HTTP/2 and can support TLS encryption for secure communication. However, RPC mechanisms might need additional security measures (things like authentication tokens) to be explicitly implemented.
REST: REST APIs benefit from standard HTTP security mechanisms, including TLS for encryption and OAuth 2.0 for authorization. RESTful services also leverage the simplicity and broad compatibility of HTTP security standards, making integration with existing security frameworks more straightforward.
RPC Use Cases
- Best suited for internal microservices or service-to-service communication, where performance, latency, and efficiency are critical.
- Common in tightly coupled systems with well-defined operations, where API consumers are known and controlled.
- Useful for high-performance applications requiring binary protocols, like real-time applications, back-end microservices, or game servers.
REST Use Cases
- Commonly used for web APIs, especially public APIs, where the client applications might be unknown or broadly distributed.
- Suitable for data-oriented operations and when interacting with various client types, including mobile and web clients, due to its flexibility and standardization.
- Ideal for resource-oriented applications that require scalability and simple interfaces with support for a variety of HTTP-based clients.
Why Choose REST
REST aligns well with a focus on building bindable, composable services that are flexible, scalable, and accessible across distributed systems. REST’s resource-oriented and stateless design aligns naturally with these goals. Here’s how REST fits those objectives effectively:
Bindable, Composable Services
When services are designed to be both bindable (discoverable and dynamically connected at runtime) and composable (aggregated to perform complex workflows). REST’s resource-oriented model provides a straightforward way to represent these services as resources with well-defined endpoints. Each service can expose itself via unique URIs, and clients can bind to these services without needing detailed knowledge of the server-side logic.
REST’s standardized HTTP methods (GET, POST, PUT, DELETE) enable consistent, loosely coupled interactions, making it easy to bind services together dynamically and support different combinations based on the application’s needs. Because REST services are discoverable via their URIs, the architecture can scale easily with new services added or existing services modified without tightly coupling clients to specific method signatures, as in RPCs.
Statelessness Supports Scalability and Flexibility
When prioritizing scalability and maintainability, REST’s stateless nature enhances both. Statelessness means that each request from a client contains all the information needed to process it, allowing the server to respond without retaining session-specific data. This enables us to scale horizontally by distributing incoming requests across multiple service instances with no session stickiness.
When creating adaptable, composable applications, statelessness also means services can be reconfigured, replaced, or scaled independently. Since REST requests are self-contained, services can be bound and unbound on the fly, enabling the dynamic composition of services without worrying about dependency on server states.
Simplified Integration with External Resources
When abstracting access to external resources like databases, message brokers, and pub-sub topics, treating these as bindable services — that can be replaced by alternatives — simplifies implementation. REST is well-suited to this abstraction model because it allows each resource (that is data store, external API, etc.) to be treated as a RESTful endpoint.
For example, accessing a database or messaging service via REST endpoints means that components interact with these resources through standardized HTTP methods, which can be easily replaced or redirected to different providers without affecting service bindings. This aligns with the goal of making external dependencies flexible and replaceable without re-architecting application components.
Familiarity and Ease of Use
REST is broadly adopted and understood, making it accessible to developers and clients alike. When components are intended to support domain-driven design and simplify service creation, REST provides a familiar interface that allows developers to easily define, bind, and compose services.
REST’s uniform interface constraint encourages consistent patterns for interacting with services, reducing the learning curve for developers and allowing them to leverage existing HTTP infrastructure.
Using REST also aligns with the objective of reducing complexity, as it standardizes communication patterns and reduces the need for protocol-specific knowledge. REST’s predictable HTTP methods and status codes help developers understand and debug interactions quickly, making services easier to develop and maintain.
Decoupled, Scalable Architecture
REST facilitates synchronous and asynchronous messaging and event publication-subscription, emphasizing scalability and decoupling. REST’s loose coupling allows services to interact in a highly decoupled manner: each service is only concerned with the data it receives and responds with, without having to know the exact details of other services. This supports the model of self-organization and runtime discovery of service dependencies, where services can be composed dynamically based on runtime needs without rigid coupling.
Additionally, REST is naturally suited to scalable distributed architectures because each service can expose a standardized RESTful API that other services can discover and consume as needed. RESTful principles simplify cross-service communication, allowing services to efficiently coordinate without explicit dependencies, enabling a more resilient and adaptable system.
Support for Real-Time and Event-Driven Architecture
REST — when combined with its cousin ECST (event-carried state transfer) — enables synchronous and asynchronous communication, particularly when including a pub-sub model using JMS. RESTful services can easily publish and subscribe to event notifications by exposing resources for polling or push-based updates.
This flexibility in REST supports the goals of creating a decoupled, event-driven system without requiring a specific protocol for real-time communication, allowing a hybrid model that combines REST with specialized messaging as needed.
Cross-Platform Compatibility and Interoperability
When positioned as an abstraction layer between application and business platforms, REST can integrate seamlessly with other platforms, like Salesforce, while supporting potential replacements for external resources. REST’s reliance on the HTTP protocol ensures broad compatibility across different systems and devices, making it easier to interact with third-party platforms, mobile applications, and legacy systems. This aligns with the goal of being platform-agnostic and maximizing reusability.
By leveraging REST, we can achieve interoperability with virtually any system that supports HTTP, giving it a substantial advantage in cross-platform integration. This flexibility is crucial for scaling, expanding services, and providing universal accessibility to those services.
Summary
REST aligns with many key objectives by:
- Supporting bindable and composable services with a resource-oriented, flexible architecture.
- Enabling stateless communication, which aids scalability and adaptability.
- Facilitating integration with external resources through standardized HTTP interactions.
- Providing a familiar interface that reduces complexity and aids developer productivity.
- Promoting decoupled architecture for dynamic service composition.
- Accommodating real-time, event-driven interactions through ECST.
- Ensuring cross-platform compatibility, critical for interoperability.
REST’s resource-oriented, stateless nature, combined with its broad compatibility and scalability, make it a natural fit for enabling flexible, composable, and highly adaptable distributed systems.
Leave the complexity of APIs like gRPC for those specific cases where maximum message data compression is the primary requirement. You might be surprised by how seldom that is really necessary.
GraphQL is not really an API and it is what its name implies: a query language (a bit like SQL). We learned back in the 1980s that embedding SQL queries in code made loose coupling impossible.
Thanks for reading!
Questions and comments are always welcome.
If you found this useful, a clap would let us know we’re on the right track.
A little related reading: