AlgoMaster Logo

Backend-for-Frontend

Medium Priority13 min readUpdated September 13, 2026
Listen to this chapter
Unlock Audio

Two clients displaying the same order may need very different information. A mobile order-details screen might need an order summary, delivery progress, and a few suggested products, while an operations dashboard needs payment history, warehouse events, and support actions. Forcing both clients through one response either sends unnecessary data or leaves each client assembling several service responses.

A backend-for-frontend gives a particular client experience an API that fits its needs.

This chapter explains how to define that boundary, compose responses, handle dependency failures, and keep client-specific behavior from becoming duplicated business logic.

1. A Backend for a Client Experience

A backend-for-frontend, or BFF, is a server-side component that adapts backend capabilities to a particular frontend. It can select fields, combine data, and expose operations that fit the client's workflows.

The boundary usually follows meaningful differences in client requirements and ownership. A mobile application and an operations dashboard may benefit from separate BFFs. Two mobile applications with the same workflows and release requirements may share one. A separate deployment for every platform or screen is not a requirement of the pattern.

Assume a commerce system has shared orders, shipping, and catalog services. The following design gives the customer mobile application and the operations dashboard their own APIs.

The BFFs can evolve their response shapes independently while using the same authoritative order and shipping operations. Shared infrastructure such as an API gateway can sit in front of them. This diagram omits it to show each component's responsibilities more clearly.

BFFs, Gateways, and GraphQL

A gateway primarily handles routing and common traffic policies. A BFF owns a client-facing application contract. A product may support both roles, but putting the code that assembles screen data into shared gateway configuration still creates application code that needs an owner, tests, and a release process.

GraphQL is an API approach, while a BFF is an architectural role. A BFF can expose GraphQL or HTTP endpoints. A shared GraphQL API may already provide enough selection and composition that another layer would add little value. The decision depends on client requirements, security, and ownership rather than the protocol name.

2. Ownership and Business Rules

The mobile team should be able to change the order-details representation without coordinating every field with the dashboard team. That independence is useful only if the BFF boundary remains clear.

Scroll
DecisionAppropriate ownerReason
Which order fields appear in the mobile responseMobile BFFThe selection serves a particular client experience
How many suggested products to includeMobile BFFThe limit depends on the screen and payload budget
Whether the caller can access an orderOrders serviceAccess depends on authoritative ownership and tenant data
Whether the caller can cancel an orderOrders serviceEvery client must obey the same state-transition rules
Whether a refund amount is validPayment or refund domain serviceFinancial rules must remain consistent across entry points
Whether the BFF may omit an optional section after it failsBFF contractThe frontend needs to know what to display

A BFF can turn a service-provided capability into a UI hint, such as canCancel. It should not recreate cancellation rules by checking whether a status string happens to equal confirmed. Even an authoritative capability is only a hint for rendering: the service must check again when the user submits the action because the order may have changed.

Keep durable business records in their owning services. BFFs may need session storage or caches, but copying order state into a BFF database creates another consistency problem. If a view needs a dedicated read model, define how it updates, how stale it can be, and which service remains authoritative.

3. A Client-Specific Response Contract

For the mobile application, define an order-details view with three sections: a required order summary, optional delivery information, and optional suggestions. The endpoint is a read-only view. It does not create an order, reserve stock, or record a payment.

Assume the native mobile client uses a bearer access token that the mobile BFF accepts. All examples use HTTPS. Credential values are placeholders, and the examples omit HTTP body lengths for readability.

A successful response contains only the information this screen needs:

The route, field names, and section states are application conventions. The orders service supplies the monetary amount and cancellation capability. The BFF selects and assembles those values; it does not recalculate the order total.

Use explicit schemas for view responses. Serializing internal service objects directly can expose a newly added internal field without a deliberate client API change. Map an allowlist of fields into the BFF representation.

Client-specific does not have to mean tied to pixel positions. Prefer concepts such as delivery over names such as bottomRightPanel. Keep amounts and dates structured so the client can format them appropriately. If the BFF returns localized display text, locale becomes an input to the representation and any cache key.

Bounded Composition

The example includes a small fixed number of suggestions. Avoid letting a caller request unlimited related data through arbitrary expansion parameters.

For collection views, bound both the page size and downstream work. Fetching 20 orders and then making a separate shipping request for each creates 21 backend calls before any other enrichment. Prefer a batch shipping lookup or an appropriate read API. The BFF should not hide an unbounded multiplication of work behind one convenient client request.

Keep pagination semantics clear. If the orders service supplies the page order and cursor, optional enrichment should not silently remove items and make the page inconsistent. Filtering or sorting on fields from the additional lookups may require a different way to query the data rather than manipulating one partial page in memory.

4. Dependency Order and Latency

Fan-out occurs when one incoming request produces several downstream calls. It can reduce client round trips, but it also makes the response depend on multiple services.

For this screen, retrieve and authorize the order first. Only then request shipping information and suggestions using identifiers the BFF obtains from the authorized order. This avoids doing unnecessary work for an inaccessible order and prevents enrichment from becoming an accidental source of information about it.

The diagram shows the dependency order. Shipping and suggestions can run concurrently after the required order result is available.

For illustration, suppose order retrieval takes 120 ms, shipping takes 90 ms, suggestions take 140 ms, and assembly takes 10 ms. Sequential calls take about 360 ms. Running the two enrichment calls concurrently takes about 270 ms: 120 + max(90, 140) + 10. These figures exclude other network and processing costs and are not performance guarantees.

The total remains sensitive to slow dependencies. Give the request an overall deadline and give optional work smaller budgets. Start each budget with the time already spent in mind; do not reset the full allowance for every call or retry.

Bound concurrent downstream calls across requests as well as within one request. A BFF receiving 1,000 requests per second with three downstream calls per request generates roughly 3,000 downstream requests per second before retries. A reduction in mobile traffic does not imply a reduction in backend load.

When the client disconnects or the request deadline expires, cancel unnecessary work where supported. Avoid retrying optional suggestions past the point where they can still contribute to the response.

The animation below compares a phone making separate service calls with a BFF combining the data into one response.

5. Partial Failures as Part of the Contract

An order-details screen can remain useful without product suggestions. It cannot remain useful without the order itself. Classify dependencies by the client behavior they support instead of treating every exception the same way.

Scroll
ConditionExample BFF behaviorClient behavior
BFF loaded the order; enrichment succeededReturn the full viewRender all available sections
Shipping call exceeded its deadlineReturn the view with delivery unavailableShow order details and a delivery fallback
Suggestions returned no matchesReturn available suggestions with an empty listShow no suggestions
BFF could not load suggestionsReturn suggestions as unavailableOmit the section or show a way to retry
Caller cannot access the orderReject the whole requestShow the documented access or not-found state
Required order call timed outFail the whole requestShow a retryable screen error under the client policy

For the same lookup, a shipping timeout can produce this successful but degraded representation:

Here, unavailable means the BFF could not obtain the section. available with an empty list means the lookup succeeded and found no suggestions. If an order needs no delivery, define a separate not_applicable state with data: null. Do not make clients guess whether null means a failure, an empty result, or an irrelevant section.

Using 200 is appropriate for this contract because the required order is present and unavailable optional sections are valid parts of the representation. 206 Partial Content is for HTTP range responses, not an instruction to render part of a screen.

If the required orders service times out while the BFF is waiting on it, the BFF can return 504 Gateway Timeout:

An upstream authorization denial is not an optional outage. Do not bypass it with cached data or a more privileged credential. For this example, an authenticated caller requesting another customer's order receives 404 Not Found, matching the service's concealment policy. This is an application choice, not a universal requirement.

Record the underlying cause of unavailable sections internally. A 200 response with broken delivery data still represents a degraded experience even though an HTTP error counter will not detect it.

6. Security at the BFF Boundary

A native mobile BFF can accept access tokens while a browser BFF uses a server-managed session. These are different authentication arrangements for the same architectural role.

In a browser OAuth arrangement, the BFF can hold access and refresh tokens server-side and give the browser an opaque session cookie. Use Secure, HttpOnly, and an appropriate SameSite setting, plus explicit cross-site request forgery protection for state-changing operations. Keeping tokens out of JavaScript reduces token theft exposure, but injected scripts can still make requests through the victim's browser. A BFF does not eliminate cross-site scripting risk.

Limit upstream destinations and operations explicitly; never expose a general proxy that attaches credentials to a caller-supplied URL. Expire and invalidate sessions deliberately, and avoid exposing tokens through responses or logs.

User Authority Across Services

A BFF's service identity and the end user's authority serve different purposes. Knowing that a request came from the mobile BFF does not prove that the user may access a particular order.

Use a supported delegation mechanism or protected identity context so services can enforce the caller's permissions. Do not forward tokens to services outside their intended audience. Do not substitute a broadly privileged service credential after a service denies a user request.

Derive identity from verified credentials or session state. Treat order IDs and any submitted tenant identifiers as untrusted input. The orders service must establish access before the BFF returns either the order or related information.

Personalized Caching

The order-details examples disable storage with Cache-Control: no-store. If a different view uses internal caching, define its isolation requirements first. A key containing only orderId may be unsafe when different callers receive different fields or capabilities.

Include the relevant tenant, identity or permission context, locale, and representation version where needed. Recheck authorization and account for permission changes before serving protected cached data. A shared public catalog cache has different requirements from a cache of personalized order views.

Never extend stale permissions merely to keep a screen available. If stale non-sensitive data is an accepted fallback, identify its age and meaning in the contract rather than presenting it as fresh.

7. Writes and Business Workflows

A BFF can adapt a client action into a domain command, but the owning service should execute the business transition. Consider cancellation of ord_784.

The mobile client submits the action through the BFF. The orders service rechecks authorization and cancellation rules, then records the result. The diagram shows this boundary without moving the order transaction into the BFF.

Malformed input can produce 400 Bad Request before delegation. For example, a cancellation body with a numeric reason fails the BFF's request schema. A well-formed cancellation for an already shipped order reaches the orders service, which can reject the state conflict with 409 Conflict under this API's policy. Preserve distinct machine-readable errors so the client can distinguish correcting input from refreshing stale order state.

Carry through any service-supported idempotency key and concurrency precondition. If the client never receives a write response, keep the same logical operation identity during a retry. Creating a new key inside the BFF for every attempt defeats duplicate protection.

Do not treat a timeout as proof that cancellation failed. The service may have committed it before the response disappeared. Let the client reconcile through the service's documented retry or status mechanism.

If cancellation requires a refund and a warehouse update, use a durable business workflow that the appropriate service owns. A BFF handler that calls both systems and hopes neither fails leaves recovery dependent on one transient HTTP request.

8. Evolution and Operational Cost

A BFF adds a deployment, network hop, dependency graph, and potential outage. It is worthwhile when it provides a useful client contract or security boundary, not merely because the system has multiple frontends.

Mobile applications often remain installed long after a new release. Deploying a new BFF does not update those clients. Keep existing fields and behavior compatible, give new optional sections safe defaults, and measure actual usage before retiring an older contract. Web deployments can also leave old tabs running, so coordinated releases do not guarantee instant client migration.

Prefer additive changes when possible. If a field's meaning must change, introduce a new field or an explicit contract version rather than relying only on a device's user-agent string. Document what old clients receive when new backend capabilities appear.

Test the assembled response at the BFF boundary, including access denial, unavailable enrichment, empty results, slow dependencies, and domain write errors. Service mocks help reproduce failures, but integration checks must also verify real identity propagation and schema compatibility. Include representative supported client versions when evaluating a breaking change.

Track downstream calls per request, end-to-end latency, unavailable-section frequency, and payload size. These show whether composition is improving the experience or merely hiding expensive backend behavior.

Keep shared libraries focused on stable concerns such as transport and tracing. A shared package containing every client's response model can force all BFFs to release together and undo the independence they should provide.

Summary

A backend-for-frontend adapts shared backend capabilities to a specific client experience. It owns response composition and client-facing behavior while services retain authoritative business rules, authorization, and durable workflows.

Bound downstream work, distinguish optional failures from missing required data, and protect identity across every call. Treat the BFF contract as an API that must remain compatible with deployed clients, even when the frontend and BFF share a team.