Skip to content

Backend-for-Frontend vs API gateway

The gateway does what every request needs; a BFF does what one user experience needs, and that experience's team runs it.

Predict first

Before you read: predict

ExamHall's mobile team is building the app's exam home screen. It shows the exam's title and start time from the exam API, the number of answered questions from the attempts API and the proctor's check-in state from the proctoring API. Today the app calls all three APIs itself, over train and café connections, and shows a few fields of each reply. The web app's home screen shows other fields from the same APIs. The API gateway in front of the APIs already checks tokens and rate limits for every client.

Where should the home screen's combined reply be built?

Pick one

The 30-second version

An API gateway is one shared front door: it checks tokens, applies limits and routes, the same for every client. A BFF is a backend owned by one frontend's team: it calls several services and returns what one screen shows. Keep screen shaping out of the gateway, and business rules out of BFFs.

The gateway does what every request needs; a BFF does what one user experience needs, and that experience's team runs it.

How it works

Step 1 of 12

Two edges. A gateway is shared infrastructure in front of many services and clients. A BFF is a server-side part of one frontend.

Analogy: A trade fair at an exhibition centre

At the trade fair In the system
the venue's entrance hall checks every badge and closes the doors when the hall is full, for every exhibitor alike the gateway's token checks, limits and routing (step 2)
each exhibitor's own staff fetch leaflets, samples and prices from their back office and hand a visitor one bag a BFF owned by one frontend team, aggregating and shaping (steps 3–5)
the venue desk starts packing one exhibitor's bags, so every exhibitor queues at the venue office for each change screens in the gateway (step 7)
a trade stand and a public stand each print the discount on their own sheets, and quote two prices after head office changes it a rule copied into BFFs drifts (step 8)

In real terms: put the work every request needs at the shared edge, and the work one screen needs in that screen's own backend.

Breaks down when: visitors can see the stand's staff at work. A BFF's calls happen out of the client's sight, inside the perimeter.

Contrasting case: a single shop with one door and one counter needs neither a venue desk nor separate stands.

Examples, run for real

4.1 ExamHall EXH-2700: three calls from the browser

EXH-2700: ExamHall's candidate app once loaded the exam home screen in the browser, calling the exam, attempts and proctoring APIs through the gateway, each reply a whole record. MR !3000 moved those calls into the app's Next.js server layer, which calls the same APIs on the server and sends the page only the screen's fields. Here a node:http stand-in plays that server layer:

4.1 EXH-2700 browser calls each API: requests over the candidate's connection 3 · bytes 1248 · API calls 3
4.1 MR !3000 server layer as BFF: requests over the candidate's connection 1 · bytes 109 · API calls 3

The APIs see the same three calls; the candidate's connection carries one (steps 4–5).

4.2 Proof domain, chat: two BFFs over one messages service

A chat company's web BFF and phone BFF read the same conversation from one messages service and look sender names up in one batch call:

4.2 web BFF: messages 40 · fields per message 6 · bytes 5517 · messages-service calls 1 · user-lookup calls 1 (7 senders)
4.2 phone BFF: messages 20 · fields per message 3 · bytes 1772 · messages-service calls 1 · user-lookup calls 1 (7 senders)

Each screen gets its own shape from the same service (step 5).

Picture it

Concept illustration for this lesson.
An illustration to help it stick. The mechanism is in the sections above.

Try it

Answer first, then check. Nothing is saved.

Question 1 of 6

A chat company allows each user 60 API requests a minute. It runs an API gateway that checks tokens and routes. Users reach its API three ways: the web app calls the web BFF, the phone app calls the phone BFF, and bots call the bot API. The two BFFs read and post messages through the messages service; the bot API runs bots' commands in the commands service. Today the web BFF and the phone BFF each count each user's requests and refuse any past 60 in a minute; the bot API counts nothing. In a test minute, user u-7 sends 50 requests from the web app, 50 from the phone app and 50 from a bot, taking turns.

Which one change admits u-7 at most 60 times in that minute, while a user who sends 60 from the phone app alone still has all 60 admitted?

Pick one

Question 2 of 6

A chat app lets a sender edit a message for 15 minutes after sending it. The web BFF and the phone BFF each check that window before they pass an edit to the messages service, which stores every edit it receives. The desktop app's team starts its own BFF next sprint and plans to copy the check into it.

What should the teams do with the check?

Pick one

Question 3 of 6

MR !3001 in ExamHall's candidate app (Next.js 16). The exam home page is a Server Component rendered on each request:

app/api/exam-home/[id]/route.ts, a Route Handler in the same app, calls the NestJS exam and proctoring APIs and merges their replies.

What should the review ask for?

Code for this question
// app/exam/[id]/page.tsxexport default async function ExamHome({ params }: { params: Promise<{ id: string }> }) {  const { id } = await params;  const res = await fetch(`${process.env.APP_ORIGIN}/api/exam-home/${id}`, { cache: 'no-store' });  return <Home data={await res.json()} />;}
Pick one

Question 4 of 6

A payments company's API gateway holds a response template for each screen of its web dashboard, its phone app and its partner portal; each template calls the ledger services and reshapes their replies. A platform team of four owns the gateway and releases it every two weeks. Template changes from the three UI teams now wait 11 days on average before they ship.

What should the architecture review recommend?

Pick one

Question 5 of 6

A ticketing company plans its backends. Its iOS and Android apps show the same screens and are built by one mobile team; its box-office kiosk prints tickets and takes cash, and is built by the venue-systems team. The plan's notes:

  • Phone apps: the iOS and Android apps share one BFF, since their screens match.
  • Kiosk: it calls the phone apps' BFF too, since all three apps sell the same tickets.
  • Ownership: the mobile team builds and runs the phone apps' BFF, since it changes with their screens.
  • Calls: the phone apps' BFF fetches the event first, then calls the seat service and the price service at the same time, since the screen waits for both.

Which note should the review push back on?

Pick one

Question 6 of 6

A streaming service keeps titles rated 18 away from kids profiles. Its gateway config, its two BFFs' play handlers, its playback service's handler and its nightly preview job are below:

# gateway.yaml
auth: verify-token
rate_limit: 60/min per user
routes:
  /web/*: web-bff
  /tv/*: tv-bff
  /support/*: playback

This evening four requests ask to play t-18, rated 18, for the kids profile p-kid: one from the web app, one from the TV app, one from a support agent's tool, which calls /support/play, and the preview job's.

How many sessions does the playback service start?

Code for this question
// web-bff, POST /web/play (profile and title are loaded from the request's ids)if (profile.kids && title.rating === '18') return reply(403);return reply(201, await forward('playback', { profileId: profile.id, titleId: title.id })); // tv-bff, POST /tv/play (profile and title are loaded from the request's ids)return reply(201, await forward('playback', { profileId: profile.id, titleId: title.id })); // playback, POST /playconst session = await db.sessions.insert({ profileId: body.profileId, titleId: body.titleId });return reply(201, session); // preview-job, run nightly inside the networkawait post('http://playback.internal/play', { profileId: 'p-kid', titleId: 't-18' });
Pick one

Go deeper

Pro tips
  1. Give each BFF route a test that counts its downstream calls per request and fails when the count grows with the list it returns. When: a screen's BFF looks a related record up for each item in a list (step 4; the N+1 shape belongs to db-n-plus-one). Verify (RAN, tips.mjs):
    tip 1 phone BFF, one user lookup per message: user-lookup calls 20 for 20 messages
    tip 1 phone BFF, one batch lookup: user-lookup calls 1 for 20 messages
Source

The Next.js pages and the IETF draft change between releases, so their claims are labelled as of 2026-10-03.

Checked against the tools and docs as of 2026-10-03.