What are RPCs?
RPC (Remote Procedure Call) systems allow software running in one process, machine, or service to invoke functionality located somewhere else as if it were a local function call.
The core idea behind RPC is abstraction. Instead of thinking in terms of resources, endpoints, or data representations, developers think in terms of calling procedures, methods, or functions. The networking details are handled by the RPC framework.
In RPC interaction:
- A client calls a remote method.
- The request is serialized and transmitted over the network.
- The remote service executes the procedure.
- The result is returned to the caller.
- The framework deserializes the response into native language objects.
RPC systems exist because many distributed systems are fundamentally action-oriented rather than resource-oriented. They provide a natural way to model service-to-service communication, business operations, workflows, and internal platform APIs.
Common use cases include:
- Microservice communication
- Internal platform APIs
- Backend-to-backend communication
- Distributed systems
- Enterprise integrations
- High-performance service meshes
- Full-stack TypeScript applications
This article includes: XML-RPC, SOAP, JSON-RPC, gRPC, Apache Thrift, Connect, tRPC
How to Choose ?
- Full-stack TypeScript development -> tRPC
- Modern microservices, HTTP/2, and streaming -> gRPC
- gRPC-style APIs needing broad HTTP compatibility (browser and backend) -> Connect
- Existing Thrift ecosystems or cross-language protocol/transport flexibility -> Apache Thrift
- Enterprise and standards-driven integrations -> SOAP
- Lightweight RPC over JSON -> JSON-RPC
- Legacy XML-RPC interoperability -> XML-RPC
flowchart TD
A[Need an RPC System] --> B{Entire stack uses TypeScript?}
B -->|Yes| C{Need end-to-end<br/>TypeScript inference?}
C -->|Yes| TRPC[tRPC]
C -->|No| D
B -->|No| D{Need broad HTTP compatibility<br/>across browser and backend clients?}
D -->|Yes| CONNECT[Connect]
D -->|No| E
E{Building internal<br/>microservices?}
E -->|Yes| F{Already using Thrift, or need<br/>flexible protocols/transports<br/>across many languages?}
F -->|Yes| THRIFT[Apache Thrift]
F -->|No| GRPC[gRPC]
E -->|No| G
G{Enterprise integration<br/>or WS-* requirements?}
G -->|Yes| SOAP[SOAP]
G -->|No| H
H{Must integrate with<br/>legacy XML-RPC systems?}
H -->|Yes| XMLRPC[XML-RPC]
H -->|No| JSONRPC[JSON-RPC]
Comparison
| Aspect | JSON-RPC | XML-RPC | SOAP | Apache Thrift | gRPC | Connect | tRPC |
|---|---|---|---|---|---|---|---|
| Mental Model | Remote method calls | Remote method calls | Service operations and contracts | Shared contracts across languages | Fast, contract-driven function calls | Service methods | Type-safe function calls |
| Transport Protocol | Usually HTTP, WebSocket, TCP | Usually HTTP | Usually HTTP | Multiple transports | HTTP/2 | HTTP/1.1 and HTTP/2 | HTTP |
| Serialization Format | JSON | XML | XML | Binary protocols | Protocol Buffers | Protocol Buffers or JSON | Native TypeScript types over JSON |
| Performance | Medium | Low | Low | High | Very High | High | Medium |
| Type Safety | Low | Low | Medium | High | High | High | Very High |
| Browser Support | Excellent | Good | Good | Limited | Limited without proxies | Excellent | Excellent |
| Language Support | Broad | Broad | Broad | Broad | Broad | Broad | TypeScript-focused |
| Complexity | Low | Low | High | Medium | Medium | Medium | Low |
| Typical Use Cases | Lightweight APIs, protocol integrations | Legacy systems | Enterprise integrations | Polyglot backend systems | Microservices, platform infrastructure | Browser-friendly RPC, modern APIs | Full-stack TypeScript applications |
| Strengths | Simplicity, easy tooling, human-readable | Simplicity and legacy compatibility | Standardization, enterprise features | Efficient cross-language communication | Performance, streaming, ecosystem | Interoperability and browser support | Outstanding developer experience |
| Weaknesses | Weak contracts and typing | Verbose XML and limited capabilities | Complexity and operational overhead | Smaller ecosystem than gRPC | Browser constraints and operational complexity | Newer ecosystem | Limited outside TypeScript |
Evolution View
| Era | Common Technologies | Primary Goal |
|---|---|---|
| Early Web Services | XML-RPC, SOAP | Standardized remote communication over HTTP |
| Distributed Service Platforms | Apache Thrift | Efficient cross-language RPC |
| Cloud-Native Systems | gRPC | High-performance service communication |
| Modern Web-Friendly RPC | Connect | Better HTTP and browser interoperability |
| TypeScript-First Development | tRPC | End-to-end type safety and developer productivity |
XML-RPC
In the late 1990s, distributed systems were difficult to integrate. Common approaches included:
CORBA
DCOM
RMI
Custom TCP Protocols
These were often:
- Vendor-specific
- Complex
- Difficult to integrate across platforms
Developers wanted something that worked over HTTP using XML. XML-RPC was created in 1998 and provided a simple way to:
- Call a function
- Across a network
- Using HTTP
It was one of the first widely adopted web-service protocols.
XML-RPC thinks in remote function calls
XML-RPC assumes: Services expose methods, and clients call those methods remotely. Instead of:
GET /users/123
POST /orders
XML-RPC says:
getUser(123)
createOrder(...)
Core assumption: Network services should look like callable functions.
Example
Request
<?xml version="1.0"?>
<methodCall>
<methodName>getUser</methodName>
<params>
<param>
<value>
<int>123</int>
</value>
</param>
</params>
</methodCall>
Response
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value>
<struct>
<member>
<name>id</name>
<value><int>123</int></value>
</member>
<member>
<name>name</name>
<value>Alice</value>
</member>
</struct>
</value>
</param>
</params>
</methodResponse>
This is effectively getUser(123) over HTTP.
Pros and Cons
| Pros | Cons |
|---|---|
| Simple RPC model | Very verbose XML payloads |
| Cross-platform interoperability | Weak typing and contracts |
| Human-readable messages | Slow parsing compared to JSON/Protobuf |
| Uses standard HTTP transport | Large message sizes |
| Easy firewall traversal | Limited modern tooling |
| Historically important | Rarely used in new systems |
| Influenced later web-service designs | Mostly replaced by newer technologies |
SOAP
As businesses started integrating systems across organizations, they needed more than simple remote procedure calls. Companies wanted:
- Security
- Reliability
- Transactions
- Formal Contracts
- Interoperability
SOAP (Simple Object Access Protocol) was introduced in 1998 and became a W3C standard in the early 2000s. SOAP attempted to standardize enterprise communication across platforms and vendors.
SOAP thinks in contracts and messages
Core assumption: Distributed systems need formal agreements about communication.
A SOAP service is:
Contract (WSDL)
+
Message Format
+
Enterprise Standards
Example
SOAP Request
<soap:Envelope>
<soap:Body>
<GetUser>
<Id>123</Id>
</GetUser>
</soap:Body>
</soap:Envelope>
SOAP Response
<soap:Envelope>
<soap:Body>
<GetUserResponse>
<User>
<Id>123</Id>
<Name>Alice</Name>
</User>
</GetUserResponse>
</soap:Body>
</soap:Envelope>
WS-* Standards
SOAP is commonly paired with a family of enterprise standards known as WS-* ("WS-star"), including:
- WS-Security – message-level encryption, signing, and authentication
- WS-ReliableMessaging – guaranteed, ordered message delivery even over unreliable networks
- WS-AtomicTransaction – coordinating distributed transactions across multiple services
These standards are why SOAP remained the go-to choice for banking, insurance, and government systems that needed guarantees plain HTTP alone couldn't provide.
Pros and Cons
| Pros | Cons |
|---|---|
| Strong formal contracts (WSDL) | Extremely verbose XML |
| Enterprise-grade security | High complexity |
| Reliable messaging support | Large payload sizes |
| Distributed transaction support | Slower than modern alternatives |
| Mature enterprise tooling | Difficult developer experience |
| Strong interoperability guarantees | Heavyweight for simple APIs |
| Common in regulated industries | Rarely chosen for new projects |
JSON-RPC
JSON-RPC first appeared around 2005, emerging as a lightweight alternative to XML-RPC and SOAP.
Why was it created?
At the time, many RPC systems used XML-RPC and SOAP which were powerful but verbose and cumbersome.
Developers wanted:
- Simpler payloads
- Easier parsing
- Better JavaScript compatibility
- Less bandwidth usage
JSON-RPC replaced XML with JSON while keeping the RPC model. JSON-RPC provided a standard way to represent:
Method Name
Parameters
Result
Error
using JSON.
JSON-RPC thinks in remote function calls
Instead of:
POST /users
GET /users/123
JSON-RPC says:
createUser(...)
getUser(123)
The server exposes a set of methods. The client invokes them. Core assumption: APIs are collections of operations, not collections of resources.
Example
Request:
{
"jsonrpc": "2.0",
"method": "getUser",
"params": {
"id": 123
},
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": {
"id": 123,
"name": "Alice"
},
"id": 1
}
The API feels like getUser(123) even though it is happening across the network.
Where JSON-RPC shows up today
JSON-RPC quietly powers several widely-used systems, including Ethereum and other blockchain node APIs, Bitcoin Core's RPC interface, and the Language Server Protocol (LSP) used by code editors like VS Code.
Pros and Cons
| Pros | Cons |
|---|---|
| Very simple protocol | Not resource-oriented |
| Lightweight JSON payloads | Limited ecosystem |
| Transport agnostic | Weak standardization outside core spec |
| Natural fit for command-style APIs | Loses many HTTP benefits |
| Supports batch requests | Can become method-heavy over time |
| Easy to implement | Less tooling than gRPC or GraphQL |
| Works well over WebSockets | No built-in typing or schema system |
gRPC
gRPC, open-sourced by Google in 2015, evolved from Stubby, Google's internal RPC framework. Google operated thousands of services communicating with each other across data centers. gRPC was designed to make service-to-service communication:
- Faster
- Strongly typed
- Easier to maintain
- Easier to generate clients for
gRPC thinks in fast, contract-driven function calls over HTTP/2
Local function:
getUser(123)
gRPC function:
userService.GetUser(123)
The client behaves as if it's calling a local function, but the execution happens on another machine.
Core assumption: "Modern service-to-service communication should feel like calling a local function, while running on HTTP/2 with strong typing, low latency, and native streaming." This is what separates gRPC from earlier RPC systems. It isn't just "remote functions," it's remote functions built for high-performance, cloud-native microservices.
Example
Service Definition
syntax = "proto3";
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
message GetUserRequest {
int32 id = 1;
}
message User {
int32 id = 1;
string name = 2;
}
Client call:
const user = await client.getUser({
id: 123
});
Response:
{
"id": 123,
"name": "Alice"
}
Because gRPC uses Protocol Buffers with numbered fields, services can add new fields over time without breaking existing clients which is a key reason contract-first systems like gRPC and Thrift age well in large codebases.
Streaming in gRPC
Unlike JSON-RPC, XML-RPC, or SOAP, gRPC supports more than simple request/response calls. It defines four call patterns:
- Unary RPC – one request, one response (a normal function call).
- Server Streaming – one request, a stream of responses (e.g., live price updates).
- Client Streaming – a stream of requests, one final response (e.g., uploading data in chunks).
- Bidirectional Streaming – both sides stream independently at the same time (e.g., chat, live collaboration).
Example of a server-streaming method:
service UserService {
rpc WatchUserUpdates(GetUserRequest) returns (stream User);
}
This built-in streaming is one of the main reasons teams pick gRPC over JSON-RPC or REST when they need continuous or real-time data flow, not just one-off calls.
Pros and Cons
| Pros | Cons |
|---|---|
| Extremely fast and efficient | Poor native browser support |
| Strongly typed contracts | Binary protocol is harder to inspect |
| Automatic client/server code generation | Requires Protobuf tooling |
| Built-in streaming support | Higher learning curve |
| Excellent for microservices | More tightly coupled contracts |
| Multi-language support | Often unnecessary for simple CRUD APIs |
| HTTP/2 performance benefits | Public APIs usually favor REST |
Apache Thrift
Apache Thrift was created at Facebook in 2007 and later donated to the Apache Software Foundation in 2008.
Facebook's infrastructure was growing rapidly and services were being written in different languages (PHP, Java, C++, Python). Each service needed to communicate with others. Without a common framework, teams had to manually create:
- Serialization formats
- Network protocols
- Client SDKs
- Server SDKs for every language combination.
Facebook wanted: "Define a service once, generate clients and servers everywhere." Thrift unified data serialization, RPC framework, and code generation into a single system.
Thrift thinks in shared contracts across languages
Core assumption: "A single Interface Definition Language (IDL) contract should let completely different languages, protocols, and transports interoperate without hand-written glue code for every combination."
Where Thrift distinguishes itself is flexibility: it doesn't lock a system into one protocol or one transport the way many RPC systems do.
Contract
↓
Generated Code (any language)
↓
Network Communication (any supported protocol/transport)
Instead of:
GET /users/123
Thrift says:
service UserService {
User getUser(1:id)
}
Core assumption: "Cross-language communication in large, heterogeneous systems should be generated from a shared contract, with the freedom to choose the protocol and transport that fits each situation."
Example
Thrift IDL
struct User {
1: i32 id
2: string name
}
service UserService {
User getUser(1:i32 id)
}
Generate code:
thrift --gen java user.thrift
thrift --gen go user.thrift
thrift --gen py user.thrift
Client:
user = client.getUser(123)
Response:
User(
id=123,
name="Alice"
)
Pros and Cons
| Pros | Cons |
|---|---|
| Strong contract-first design | More complex than REST |
| Excellent multi-language support | Weak browser support |
| High-performance binary serialization | Smaller ecosystem than gRPC |
| Automatic code generation | Additional operational complexity |
| Flexible protocols and transports | Steeper learning curve |
| Mature RPC framework | Overkill for simple applications |
| Good fit for polyglot systems | More moving parts than REST |
Connect
Connect was created by Buf Technologies and publicly introduced around 2021–2022.
Native gRPC works well between servers but struggles in browsers because browsers do not expose the full HTTP/2 capabilities that gRPC relies on. Developers had to build extra components for browser support.
Connect keeps the good parts of gRPC while making APIs work across browsers, mobile apps, backend services, and CLI tools, without requiring specialized infrastructure. Importantly, Connect isn't just "gRPC for browsers". Many teams adopt it purely for backend-to-backend communication, because it works over standard HTTP/1.1 and HTTP/2 without the operational overhead (proxies, specialized load balancers) that plain gRPC often requires.
Connect thinks in RPCs over standard HTTP
Core assumption: RPC contracts should work everywhere HTTP works, for browser and backend clients alike. Instead of choosing between:
REST for browsers
gRPC for services
Connect tries to provide:
One Contract
One API
Every Client
Example
Service Definition
Same .proto file as gRPC:
syntax = "proto3";
service UserService {
rpc GetUser(GetUserRequest)
returns (User);
}
Generate client/server code.
Client:
const response =
await client.getUser({
id: 123,
});
console.log(response.name);
The difference is largely in transport and compatibility. Connect defines a protocol that works over ordinary HTTP. Unlike gRPC, it does not require specialized HTTP/2 behavior for basic usage.
Pros and Cons
| Pros | Cons |
|---|---|
| Browser-friendly RPC | Smaller ecosystem |
| Reuses existing Protobuf contracts | Less industry adoption |
| Strong typing and code generation | Primarily useful for gRPC-style architectures |
| Works with standard HTTP infrastructure | Fewer learning resources |
| Supports Connect, gRPC, and gRPC-Web | Additional technology choice |
| Easier deployment than gRPC-Web stacks | Not as universally recognized as REST |
| Good full-stack developer experience | Can be overkill for simple APIs |
tRPC
Modern TypeScript applications often looked like this:
Frontend
|
REST/GraphQL
|
Backend
Even though both sides were written in TypeScript, developers still had to:
- Define API routes
- Create schemas
- Generate types
- Synchronize contracts This often felt redundant. Example:
type User = {
id: number;
name: string;
}
might need to be defined multiple times across frontend and backend. tRPC was created by Alex Johansson around 2021. It uses TypeScript itself as the contract.
tRPC thinks in TypeScript functions
Core assumption: The frontend and backend belong to the same codebase and can share types directly. tRPC uses TypeScript types as the source of truth.
Example
Server
const appRouter = router({
getUser: publicProcedure
.input(z.number())
.query(({ input }) => {
return {
id: input,
name: "Alice",
};
}),
});
Client
const user =
await trpc.getUser.query(123);
console.log(user.name);
TypeScript automatically knows user.id and user.name without code generation.
Pros and Cons
| Pros | Cons |
|---|---|
| End-to-end type safety | TypeScript-only ecosystem |
| No code generation required | Weak cross-language support |
| Excellent developer experience | Tight frontend/backend coupling |
| Fast development speed | Not ideal for public APIs |
| Great autocomplete and inference | Smaller ecosystem |
| Perfect for monorepos | Less suitable for large microservice environments |
| Minimal boilerplate | Harder to integrate with non-TypeScript systems |
Key Takeaways
- JSON-RPC – Choose it for lightweight, transport-agnostic method calls, especially when you already have a JSON-friendly client and don't need strong typing or contracts.
- XML-RPC – Only relevant when integrating with legacy systems that already speak it. Avoid for new projects.
- SOAP – Choose it when you need formal contracts (WSDL) and enterprise-grade guarantees like security, reliable messaging, or distributed transactions (WS-*) like banking, insurance, and government systems.
- Apache Thrift – Choose it for large, polyglot backend systems, especially if you need protocol/transport flexibility or already have Thrift infrastructure in place.
- gRPC – Choose it for modern, performance-sensitive microservices that benefit from HTTP/2, strong typing, and native streaming. Avoid for public browser-facing APIs without a proxy layer.
- Connect – Choose it when you want gRPC-style contracts with simpler HTTP compatibility across browsers, backends, and mobile clients, without the operational overhead of plain gRPC.
- tRPC – Choose it for full-stack TypeScript applications, especially monorepos, where end-to-end type safety and developer speed matter more than cross-language support.
Top comments (0)