REST vs GraphQL vs gRPC: Which API Style Should You Actually Use in 2026?
REST vs GraphQL vs gRPC: Which API Style Should You Actually Use in 2026?
REST vs GraphQL vs gRPC: Which API Style Should You Actually Use in 2026?
REST vs GraphQL vs gRPC: Which API Style Should You Actually Use in 2026?
A no-fluff breakdown with real code, benchmarks, and a decision table.

Every backend engineer hits this decision. Here’s how to make it fast and correctly.

1. REST — Still the Default, For Good Reason
Use it when: Public API, simple CRUD, external consumers, browser clients.
GET /api/v1/users/123
Authorization: Bearer <token>
{
"id": 123,
"name": "Salim Hassan",
"email": "salim@example.com",
"role": "admin",
"createdAt": "2024-01-15T10:30:00Z"
}
Node.js (Express) example:
app.get('/api/v1/users/:id', authenticate, async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
The real REST problem: over-fetching and under-fetching.
Imagine a mobile screen that only needs name and avatar. REST still returns the full user object — every field, every time. Now multiply that by 50 requests per page load.
2. GraphQL — Let the Client Ask for Exactly What It Needs
Use it when: Multiple clients (mobile, web, third-party) need different data shapes from the same API.
# Client asks only for what it needs
query GetUserProfile {
user(id: "123") {
name
avatar
posts(last: 3) {
title
publishedAt
}
}
}
Response — nothing extra:
{
"data": {
"user": {
"name": "Salim Hassan",
"avatar": "https://cdn.example.com/avatar.jpg",
"posts": [
{ "title": "Building with AI", "publishedAt": "2026-05-10" }
]
}
}
}
Node.js (Apollo Server) resolver:
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.usersAPI.getUserById(id);
},
},
User: {
posts: async (parent, { last }, { dataSources }) => {
return dataSources.postsAPI.getRecentByUser(parent.id, last);
},
},
};
Watch out for:
- N+1 problem — a query for 10 users triggers 10 separate DB calls for each user’s posts. Fix it with
DataLoader:
const userPostsLoader = new DataLoader(async (userIds) => {
const posts = await db.posts.findByUserIds(userIds);
return userIds.map(id => posts.filter(p => p.userId === id));
});
3. gRPC — When You Need Raw Speed Between Services
Use it when: Internal microservice-to-microservice communication where performance is critical.
Why it’s fast:
- Uses Protobuf (binary serialization) — 5–10x smaller payload than JSON
- Built on HTTP/2 — multiplexed streams, no head-of-line blocking
- Generated client/server code — no manual parsing
Step 1 — Define your contract (.proto file):
syntax = "proto3";
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
rpc StreamUsers (UserRequest) returns (stream UserResponse); // server streaming
}
message UserRequest {
string user_id = 1;
}
message UserResponse {
string id = 1;
string name = 2;
string email = 3;
string role = 4;
}
Step 2 — Server (Node.js):
const server = new grpc.Server();
server.addService(UserService, {
getUser: async (call, callback) => {
const user = await db.users.findById(call.request.user_id);
if (!user) return callback({ code: grpc.status.NOT_FOUND });
callback(null, user);
},
});
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
server.start();
});
Step 3 — Client (another microservice):
const client = new UserServiceClient(
'user-service:50051',
grpc.credentials.createInsecure()
);
client.getUser({ user_id: '123' }, (err, response) => {
if (err) console.error(err);
console.log(response.name); // "Salim Hassan"
});
Performance Reality Check
Rough numbers from internal benchmarks (same hardware, same data):
Metric REST (JSON) GraphQL (JSON) gRPC (Protobuf) Payload size (200 field object) ~1.2 KB ~0.9 KB ~120 bytes Serialization time ~0.8ms ~0.9ms ~0.05ms Latency p50 ~8ms ~9ms ~2ms Latency p99 ~45ms ~50ms ~12ms Throughput (req/s) ~12,000 ~10,000 ~80,000
gRPC is not slightly faster. It’s a different category.
When NOT to Use Each One
Don’t use REST when:
- Different clients need radically different data shapes
- You’re building service-to-service communication at high volume
- You’re sick of maintaining multiple endpoint versions
Don’t use GraphQL when:
- Your API is simple CRUD — you’re adding complexity for zero gain
- Your team is small and the learning curve will slow you down
- You don’t have time to set up proper caching (GraphQL breaks HTTP caching)
Don’t use gRPC when:
- Browser clients need to call you directly (gRPC-Web exists but adds friction)
- Your consumers are external third parties — they won’t want to deal with
.protofiles - Your team has no experience with Protobuf — debugging binary payloads is painful
The Real-World Architecture Pattern in 2026
Most mature systems are not using just one. They’re using all three — each in the right place:
[Browser / Mobile App]
|
| REST or GraphQL (external-facing)
↓
[API Gateway]
/ \
/ \
REST GraphQL
(public API) (BFF for mobile)
|
| gRPC (internal)
┌──────┴───────┐
[User Service] [Order Service] [Notification Service]
- GraphQL as a Backend-for-Frontend (BFF) layer — mobile and web get exactly what they need
- REST for public API consumers and simple endpoints
- gRPC for all internal service-to-service calls where performance matters
Decision Flowchart (Plain Text)
Is it external-facing (browser, mobile, third-party)?
└─ YES → Do clients need flexible data shapes?
└─ YES → GraphQL
└─ NO → REST
└─ NO (internal service-to-service) → gRPC
Quick Cheat Sheet
REST: GET /users/:id → JSON → Simple, universal, well-understood
GraphQL: query { user(id) { name, posts { title } } } → Flexible, client-driven
gRPC: .proto → Binary → Fast, typed, internal use
메타데이터
- post_id
- 94de1e9152fa
- slug
- rest-vs-graphql-vs-grpc-which-api-style-should-you-actually-use-in-2026-94de1e9152fa
- url
- https://medium.com/@tarxemo/rest-vs-graphql-vs-grpc-which-api-style-should-you-actually-use-in-2026-94de1e9152fa
- canonical_url
- https://medium.com/@tarxemo/rest-vs-graphql-vs-grpc-which-api-style-should-you-actually-use-in-2026-94de1e9152fa
- author_url
- https://medium.com/@tarxemo
- status
- ok
- fetched_at
- 2026-06-09 15:37:30