React + Spring Full-Stack Architecture: The BFF Pattern

The BFF (Backend for Frontend) pattern between a React frontend and Spring backend: when it is needed, identity and session management, aggregation, contract discipline and comparison with alternatives.

Editor window showing BFF layer code between a React client and Spring services

What is a BFF and which problem does it solve?

A BFF (Backend for Frontend) is a thin backend layer dedicated to one specific frontend — web, mobile, any client without a shared denominator — shaped by that frontend's needs. The problem it solves is clear: general-purpose APIs fit no client perfectly; clients either over-fetch, make too many calls, or copy business logic into themselves. The BFF resolves that mismatch at the server layer closest to the client.

When BFF, when direct API?

Situation

Direct API suffices

BFF signal

Client count

One web client

Web + mobile + third parties with different needs

Calls per page

1-2 endpoints

One screen aggregates 5+ services

Identity model

Simple token flow is enough

httpOnly session in browser, tokens kept server-side

Response shaping

Backend DTOs fit the screens

Screen model differs markedly from backend model

Team structure

One full-stack team

Frontend team can own its server layer

The BFF's three main jobs

Aggregation: collecting what a screen needs from multiple Spring services and returning it as one response shaped as the screen model — call orchestration and waterfall latency disappear from the client. Adaptation: arranging field names, formats and pagination for the screen; preserving the screen contract even when backend contracts change. The identity boundary: session management without leaking tokens to the browser — covered below. The shared rule across all three: no business rules in the BFF; rules live in domain services, the BFF is an experience layer only. When that line blurs, the BFF becomes a second, hard-to-maintain monolith.

Identity and sessions: the BFF's strongest justification

The model that fundamentally resolves the SPA token-storage dilemma (localStorage's XSS exposure) is tokens never reaching the browser at all: the BFF drives the OAuth/OIDC flow; access/refresh tokens are held server-side; between browser and BFF only an httpOnly + Secure + SameSite session cookie travels. The React client never sees a token; the BFF attaches tokens to downstream calls itself. In security reviews this pattern is the cleanest answer to "where are tokens stored?" — and it requires CSRF protection (SameSite + tokens) to be built correctly in the BFF.

Implementation on the Spring side

In the Spring ecosystem a BFF is built along two mature paths: a reactive service on WebFlux/WebClient for highly concurrent aggregation, or a simple blocking model on Java 21 virtual threads — both handle parallel downstream calls efficiently. Spring Cloud Gateway offers a ready skeleton when routing + filter needs approach the BFF's; the OAuth client support (spring-security-oauth2-client) assembles the session pattern above from standard components. The resilience minimum: a timeout on every downstream call, a partial-failure strategy (per-section errors instead of failing the whole screen) and circuit breakers.

Contract discipline: the screen model is an API too

The screen models a BFF returns are the formal contract between frontend and BFF: define them with OpenAPI, generate TypeScript types from that definition, and version breaking changes. GraphQL is the alternative solution to the same problem — flexible query power in exchange for server-side complexity (N+1 resolvers, query cost control); if the team lacks GraphQL maturity, a well-designed REST BFF is more predictable in most enterprise scenarios.

Business impact: team velocity, architecturally

The BFF's return is measured in screen development speed: the frontend team adds fields and reshapes responses in its own layer without waiting for the backend sprint; mobile and web evolve without breaking each other's API. The cost must be written honestly too: one more service to operate, one more pipeline. Our decision criterion is practical: if calls-per-screen, token storage needs and client diversity point to BFF in the table, the investment pays back quickly; if not, direct APIs with good DTO design are enough.

Frequently asked questions

Is a BFF the same as an API gateway?

No: a gateway is client-agnostic horizontal infrastructure (routing, rate limiting, TLS); a BFF is an experience layer dedicated to one frontend. They can be used together.

Is a separate BFF per client mandatory?

The pattern's name suggests it, but pragmatism rules: clients whose needs truly diverge earn separate BFFs; two similar web experiences can share one.

Do Next.js API routes count as a BFF?

Functionally yes — they can take on the same jobs. In an enterprise Java ecosystem, a Spring-based BFF usually integrates better with team skills, observability and security standards.

How do we prevent business logic drifting into the BFF?

With a written rule and code review: composition and formatting allowed, business rules and persistent state forbidden. The test: if the same rule is needed by a second client, its home is the domain service.

BFF checklist

  1. BFF need validated against the decision table

  2. Tokens server-side; browser holds only an httpOnly session cookie

  3. CSRF protection (SameSite + tokens) built in the BFF

  4. Timeouts + circuit breakers + partial-failure strategy on downstream calls

  5. Screen models defined in OpenAPI; TS types generated

  6. Business-rule boundary written; enforced in code review

  7. BFF metrics (per-screen latency, downstream breakdown) dashboarded

SSH Yazılım designs and implements BFF architectures end to end in React + Spring projects. Let us sharpen your frontend-backend boundary together.