younifyd
← All articles
Engineering8 min read

GraphQL vs REST in a BFF Layer: Which Should You Use and When?

y

The younifyd team

July 8, 2026 · younifyd.com

The Backend-for-Frontend pattern solves a real mismatch: what your upstream services expose is rarely what a given client actually needs. The BFF sits between clients and services, composing, shaping, and optimizing data for each channel. But once you've decided to build a BFF, a second decision follows immediately: should the surface you expose to clients be REST or GraphQL? It's not a trivial choice — it affects how clients fetch data, how the BFF caches responses, how you version the API, and how fast frontend teams can ship independently. Get it wrong for your context and nothing breaks right away; it just compounds, making every later feature a little harder than it should be. This post works through the tradeoffs with concrete commerce examples and ends with a framework for making the call in your own context, rather than a blanket recommendation.

What the BFF layer is actually doing

Before comparing the two API styles, it's worth being precise about what a BFF does in a commerce system. A product page request needs data from a product catalog service, an inventory system, a pricing engine, a recommendation service, and a review aggregator — five separate upstream services, each with its own schema, auth, and response shape. The BFF receives the client request, fans out to all five in parallel, merges the results, filters to the fields the client actually needs, and returns one response. The client makes one call; the BFF makes five. That composition work happens regardless of whether the surface is REST or GraphQL — the REST-vs-GraphQL question is specifically about the contract between the BFF and its clients, not what the BFF does internally. Calls from the BFF to the catalog, inventory, and pricing services will almost certainly stay REST calls no matter which style you pick for the client-facing surface.

The case for REST in your BFF

REST remains the right choice for most BFF layers, for practical rather than theoretical reasons. REST endpoints map naturally to the bounded contexts a BFF serves — a product page endpoint, a cart endpoint, a checkout endpoint — each purpose-built, returning exactly the fields that use case needs, and independently cacheable at the HTTP layer. That caching is REST's biggest advantage: a GET can be cached at a CDN, a reverse proxy, or the BFF's own response cache, so when ten thousand users load the same page in a minute, the upstream calls happen once — not ten thousand times. GraphQL queries sent as POST requests, the dominant pattern, don't get this for free; caching moves into the application layer, where it's harder to get right. REST endpoints are also simpler to reason about on-call — one slow endpoint in your dashboard, no query introspection or resolver tracing. And for mobile clients, REST gives direct control over payload size: a mobile endpoint returns a small image URL, the web endpoint for the same product returns a high-resolution one, without every resolver needing per-client logic on top of a client-defined query.

GET /bff/product-page/p-421 → cached at CDN/proxy
10,000 req/min → ~1 upstream fan-out (cache warm)
POST /graphql {...} → not CDN-cacheable
10,000 req/min → 10,000 upstream fan-outs

REST BFF: where it gets painful

REST BFFs have a well-known failure mode: endpoint proliferation driven by UI requirements. A product page gets an endpoint; the page adds a section needing three more fields, so the frontend team adds them; a feature is removed from one platform but not others, and the fields stay forever because removing them would be a breaking change. Six months later the BFF has forty endpoints, half returning fields nobody uses, and every new frontend feature needs a backend change. This is the over-fetching and under-fetching problem GraphQL was designed to solve. The honest fix in a REST BFF is disciplined endpoint design — separate endpoints per distinct UI view, resisting the urge to reuse an endpoint across contexts just because the data overlaps, and treating BFF endpoints as a UI-specific contract rather than a general-purpose API. That's achievable, but it takes ongoing discipline not every team maintains.

over-fetching product-list returns 40 fields, UI shows 8
under-fetching checkout misses loyalty tier → 2nd BFF call
coupling new UI field → backend deploy required

The case for GraphQL in your BFF

GraphQL at the BFF layer solves the frontend-backend coupling problem directly: when a frontend team needs a new field, they add it to the query — no backend deployment, no coordinating with the BFF team, no waiting on a sprint. The BFF exposes a schema, the client declares what it needs, the BFF resolves it. This is most valuable when one large org has frontend teams moving faster than backend teams, or when multiple frontend teams — web, mobile, partner portal — are developing simultaneously and can't wait on each other's endpoint requests. GraphQL also handles product comparison elegantly: a REST endpoint optimized for a single product page doesn't compose naturally for comparing three products, while a GraphQL query fetches exactly the comparison fields in one request and documents its own intent. For a catalog with real heterogeneity — configurable attributes, variant combinations, context-specific pricing — GraphQL's schema-driven approach handles that variation better than a fixed REST response shape.

GraphQL BFF: where it gets painful

The practical difficulties of GraphQL at the BFF layer get less attention than its theoretical advantages. First, the N+1 problem: a query on a product list with a per-product resolver field — inventory status, say — makes a naive resolver call once per product, so twenty products generate twenty calls instead of one batched call; batching patterns fix this but add complexity a REST BFF's explicit parallel calls avoid entirely. Second, caching: GraphQL-over-POST gets none of REST's HTTP caching, so caching moves into the application layer — resolver results cached by field and argument — which is harder to get right, including bugs like a cached resolver serving stale data to a query that never expected it to be cached. Third, arbitrary client queries create unpredictable server load: a query can join deeply nested types and trigger dozens of upstream calls, where a REST BFF has a known, bounded set of calls per endpoint; depth limiting and complexity analysis become necessary controls a REST BFF doesn't require. Fourth, tooling gets harder: a REST BFF gives distinct operations per endpoint in your dashboard, while a GraphQL BFF routes everything through one endpoint, so isolating slow queries needs operation-level tracing that only works if every client names every query.

A concrete e-commerce comparison

Consider a product listing page in a headless commerce storefront: a grid of cards showing name, image, customer-specific price, stock status, and a promotional badge. With a REST BFF, one call fans out in parallel to the catalog, pricing, and promotion services and returns exactly the five fields per card the UI needs — cacheable at the CDN by category and page. Typical response time: well under 100ms. With a GraphQL BFF, one query names the same fields; price and stock status are per-product resolvers, so without batching that's twenty pricing calls and twenty inventory calls for twenty products, dropping to one each with proper batching — but the response isn't CDN-cacheable, since it's a POST, and an application-level cache on the stock resolver risks stale data. REST wins this case clearly. Now flip to the product detail page for a configurable product with thirty variant attributes, where the frontend wants to add a new field — a sustainability score, say — without waiting on a BFF release. With REST, that's a code change, a review, and a deploy; the frontend waits days. With GraphQL, if the schema already models that field, the frontend adds it to their query and ships with no BFF deployment at all — this is where GraphQL's value is most tangible.

Versioning: REST vs GraphQL at the BFF boundary

API versioning is where BFF teams most often underestimate the difference between the two approaches. REST has a well-understood pattern: route-based versioning, with both versions running simultaneously and a deliberate, tracked deprecation timeline. GraphQL handles it differently — the schema is meant to evolve without breaking changes, by adding fields rather than modifying existing ones and deprecating old fields instead of removing them. That works well when the schema was designed carefully from the start, and poorly when early decisions were wrong, because fixing those means either a breaking change or carrying an awkward schema forever. For a BFF, the real question is whether you can coordinate client upgrades: a web frontend you deploy continuously can adopt a new query shape immediately, but a native app with users still on a build from eighteen months ago cannot. If your BFF serves mobile clients you don't fully control, REST's explicit version paths are operationally simpler than maintaining a GraphQL schema that must stay backward-compatible indefinitely.

The hybrid approach: shared orchestration, different surfaces

The most pragmatic architecture for larger commerce platforms is a hybrid: REST-based orchestration internally — calling upstream services over REST, with parallel execution and caching — while the surface exposed to web clients is GraphQL and the surface exposed to mobile clients is purpose-built REST. That's not a compromise, it's matching the tool to the job: the orchestration layer benefits from REST's simplicity and batching, the web frontend benefits from GraphQL's query flexibility, and the mobile app benefits from predictable, cacheable payloads. Crucially, the component that fans out to upstream services and handles partial failures is the same regardless of which protocol sits in front of it — which is exactly the layer a visual, AI-assisted workflow builder like younifyd's is built for. You build the fan-out to your catalog, inventory, and pricing services once, as a workflow, and expose that same workflow as a REST API route for your BFF, as an MCP tool an AI shopping agent can call, or as a webhook trigger — without rebuilding it for each surface. The protocol choice at the edge stays a pure API design decision, decoupled from how the upstream coordination is built.

A decision framework

Use this framework for your own context — the honest answer for most teams is REST, with a narrow set of circumstances where GraphQL genuinely pays off.

Choose REST when:
— mobile clients with real version lag
— CDN caching matters (high-traffic, read-heavy pages)
— small team, low BFF/frontend coordination overhead
— a focused, stable set of UI views
— on-call simplicity and observability are priorities
Choose GraphQL when:
— multiple frontend teams iterating independently
— highly heterogeneous product data
— frontend velocity is blocked by BFF deploy cycles
— continuously-deployed web clients, not version-lagged mobile
— your team can run batching and query controls correctly
Choose hybrid when:
— you have both web and mobile with different iteration speeds
— orchestration performance/caching is non-negotiable
— you want web flexibility without sacrificing mobile performance

Build your BFF layer — REST, GraphQL, or both

Build the upstream orchestration once in younifyd's visual workflow builder — the fan-out, the caching, the error handling — then expose it as a REST API route for your BFF, as an MCP tool, or as a webhook trigger. The protocol at the edge stays a client decision, not an infrastructure one.

Try for Free