Frontend API Layer Architecture: REST, GraphQL, gRPC-Web, and Backend-for-Frontend (BFF) Pattern
Evaluating protocol wire efficiency, schema type safety, Protobuf serialization, and BFF orchestration layers
Part 7 in Series — Catch up on the previous article: State Management Architecture: Normalized Stores, Signals, XState Automata, and Immutability (Part 6) before diving into this post.
When a logistics enterprise launched its new mobile web portal in Latin America, engineering was blindsided by user churn. Over 68% of users abandoned the application during initial loading.
Debugging revealed that rendering the home dashboard required the client to make 14 sequential REST calls across 6 internal microservices: fetching user permissions, active shipments, driver status, notifications, and weather updates. Over high-latency 3G cellular connections with 250ms round-trip times (RTT), the network waterfalls created an 11-second delay before the UI became interactive. Worse, the JSON payloads totalled 4.2 Megabytes—of which the mobile UI consumed less than 5%.
The team had coupled the frontend UI directly to internal backend microservice boundaries.
Resolving network waterfalls and payload bloat requires designing a robust client API architecture: evaluating REST vs GraphQL vs gRPC-Web wire efficiency, and deploying the Backend-for-Frontend (BFF) pattern.
1. Protocol Comparison: REST vs GraphQL vs gRPC-Web vs tRPC
+--------------------------------------------------------------------------------+
| Frontend API Protocol Comparison |
+-------------------+--------------------+--------------------+------------------+
| Feature | REST (JSON) | GraphQL | gRPC-Web |
+-------------------+--------------------+--------------------+------------------+
| Wire Format | Text / JSON | Text / JSON | Binary (Protobuf)|
| Transport | HTTP/1.1 or HTTP/2 | HTTP/1.1 or HTTP/2 | HTTP/2 |
| Over-fetching | Common | Zero (Client Query)| Zero (Schema) |
| Schema Type Safety| Manual (OpenAPI) | Built-in Schema | Strict `.proto` |
| Browser Native | Yes | Yes | Requires Proxy |
+-------------------+--------------------+--------------------+------------------+
1.1 REST: Simplicity vs Payload Over-Fetching
REST over HTTP/JSON is universally supported and leverages browser HTTP caching (Cache-Control, ETag). However, REST endpoints suffer from:
- Over-fetching: Returning a 50KB JSON object containing 40 database fields when the mobile card only needs
idandtitle. - Under-fetching (N+1 HTTP Requests): Requiring a call to
/api/users/123, followed by separate calls to/api/orders?userId=123.
1.2 GraphQL: Client-Driven Declarative Data Fetching
GraphQL eliminates over-fetching by allowing the frontend to specify the exact fields required in a single query:
# Client Query: Fetches ONLY required fields across relational graphs in ONE HTTP call
query GetShipmentDashboard($userId: ID!) {
user(id: $userId) {
name
activeShipments(limit: 5) {
trackingId
status
destinationCity
}
}
}
Architectural Gotcha: GraphQL queries execute via POST /graphql, bypassing standard browser and CDN HTTP GET caching infrastructure. Caching must be handled via normalized client caches (Apollo Client, Relay).
1.3 gRPC-Web: High-Performance Binary Protocol Buffers
gRPC-Web enables browsers to invoke gRPC services directly using compact Protocol Buffer (Protobuf) binary serialization over HTTP/2.
// Proto3 Schema Contract (shipment_service.proto)
syntax = "proto3";
package shipment;
message ShipmentRequest {
string user_id = 1;
}
message ShipmentResponse {
string tracking_id = 1;
string status = 2;
string destination_city = 3;
}
service ShipmentService {
rpc GetActiveShipments (ShipmentRequest) returns (stream ShipmentResponse);
}
Protobuf serializes field keys into compact 1-byte integer tags (1, 2, 3) instead of repetitive JSON key strings, reducing payload sizes by 60% to 80% compared to JSON.
2. The Backend-for-Frontend (BFF) Architectural Pattern
In microservice architectures, forcing the frontend client to query internal backend microservices directly creates severe performance and security flaws:
BAD: Direct Client-to-Microservices Connection
[ Browser Client ] ----+----> [ User Microservice ]
+----> [ Order Microservice ]
+----> [ Billing Microservice ]
The Backend-for-Frontend (BFF) Pattern introduces a dedicated node service owned and maintained by the frontend team:
OPTIMAL: BFF Architectural Pattern
[ Mobile / Web Client ]
| (Single Optimized TLS Connection / Secure Cookie)
v
[ BFF Layer (Node.js / Edge Worker) ]
| (High-Speed Internal Data Center Network / gRPC)
+----> [ User Service ]
+----> [ Order Service ]
+----> [ Billing Service ]
2.1 Complete Production Node.js BFF Implementation
// Production BFF Aggregation Endpoint (Node.js Express / Fastify)
import express from "express";
import { fetchUserProfile, fetchActiveShipments } from "./internal-grpc-clients";
const bffRouter = express.Router();
bffRouter.get("/api/dashboard", async (req, res) => {
try {
const userId = req.session?.userId;
if (!userId) {
return res.status(401).json({ error: "Unauthorized" });
}
// Execute internal microservice gRPC calls IN PARALLEL over high-speed datacenter LAN
const [userProfile, shipments] = await Promise.all([
fetchUserProfile(userId),
fetchActiveShipments(userId)
]);
// Shape payload specifically for client UI requirements (Trimming unused backend fields)
const clientPayload = {
user: {
name: userProfile.displayName,
avatar: userProfile.avatarUrl
},
shipments: shipments.map((s) => ({
id: s.trackingNumber,
status: s.currentStatus,
city: s.destination.city
}))
};
return res.json(clientPayload);
} catch (error) {
console.error("BFF Aggregation Failure:", error);
return res.status(500).json({ error: "Internal Gateway Error" });
}
});
Summary & Key Takeaways
- REST Protocol: Excellent HTTP caching compatibility, but vulnerable to payload over-fetching and N+1 request waterfalls.
- GraphQL Protocol: Prevents over-fetching and provides client-driven data fetching, but requires normalized client-side caches (Apollo/Relay) due to
POSTendpoint limitations. - gRPC-Web Protocol: Delivers maximum wire efficiency using compact Protobuf binary serialization over HTTP/2.
- BFF Pattern: Decouples client UIs from internal microservice churn, aggregates network calls, and securely manages authentication tokens at the network edge.
References & Further Reading
- Fowler, M. (2015). Pattern: Backends For Frontends (BFF). MartinFowler.com.
- GraphQL Foundation. GraphQL Specification (October 2021 Edition). GraphQL Spec.
- gRPC Authors. gRPC-Web Protocol Specification. gRPC GitHub.
Part 8: Frontend Security Architecture: XSS Mitigations, CSRF Defense, Content Security Policies, and OAuth2 PKCE
Continue to Part 8 →