Lesson 12 of 30
A general-purpose backend can't be perfectly shaped for every client at once — a BFF is a thin layer the frontend team owns, whose entire job is fixing that for exactly one client.
13 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
What problem does a BFF (backend-for-frontend) most directly solve?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A single general-purpose backend often has to serve several genuinely different clients at once — a web app, a mobile app, maybe a third-party integration — and those clients frequently want different data in different shapes. Shaping one set of API responses to be perfect for all of them simultaneously is often impossible: what a data-dense web dashboard wants (lots of fields, in one big response) can be exactly what a bandwidth-constrained mobile client doesn't want.
A BFF (backend-for-frontend) is a thin backend service, owned by (or sitting close to) a specific frontend team, placed between that frontend and the general-purpose backend(s) it ultimately reads from. Its entire job is reshaping and aggregating whatever the general-purpose backend returns into exactly the shape that one specific frontend needs — nothing more general than that.
Three concrete benefits fall out of this:
Without a BFF, the frontend makes three separate calls and assembles the result itself:
const [order, customer, shipment] = await Promise.all([
fetch(`/orders/${id}`).then((r) => r.json()),
fetch(`/customers/${order.customerId}`).then((r) => r.json()),
fetch(`/shipments?orderId=${id}`).then((r) => r.json()),
]);
(And the customer call actually can't run in parallel with the order call above, since it needs order.customerId first — a real waterfall.)
A BFF endpoint built for exactly this screen does the aggregation server-side and returns one response:
// Inside the BFF, not the frontend
app.get("/bff/order-summary/:id", async (req, res) => {
const order = await ordersService.get(req.params.id);
const [customer, shipment] = await Promise.all([
customersService.get(order.customerId),
shipmentsService.getByOrder(order.id),
]);
res.json({ data: { order, customer, shipment } });
});
The frontend now makes exactly one request and gets exactly the shape that screen needs.
"How would you handle a mobile app and a web app needing very different shapes from the same backend?" is a real system-design-adjacent question, and naming the BFF pattern — plus its real operational cost — shows you understand it as a genuine architectural tradeoff, not a default best practice to reach for everywhere.