The Framework Server Layer Is Not Your BFF

Every modern full-stack framework now ships with server-side execution built in. Next.js has API routes, server actions, and RSC. Nuxt has Nitro server routes, which can simultaneously handle SSR rendering and act as a BFF gateway. SvelteKit has server load functions and +server.ts endpoints. All three are perfectly capable of serving as a lightweight BFF — I’m not going to argue otherwise.

The question isn’t whether they can do it. It’s what happens to your boundaries once they do.


The framework server layer occupies an awkward position by design. On one side, it’s responsible for aggregating data for the browser and shaping responses for the UI. On the other, it’s making outbound requests to backend services, holding credentials, and touching risk-bearing business logic. These two concerns have different security requirements, different observability needs, and very different testing strategies. Collapse them into one layer and you end up with a Nuxt server route — or a Next server action — that’s simultaneously doing data transformation, calling an internal API, and quietly handling an auth flow. Then someone asks how to unit-test the token validation, and suddenly the conversation gets uncomfortable.

I’ve run Nuxt projects where Nitro server routes handled both the frontend requests and the gateway forwarding, and it felt clean at the start. When the backend services multiplied and auth context started branching, that “lightweight gateway” turned out to be quite heavy — it just lacked visible edges to make the weight obvious.


The decision rule is straightforward: who faces the browser, who faces the backend, who owns the risk.

Browser-facing logic belongs in the framework server layer — page-level data aggregation, response shaping, simple session reads. This stuff is tightly coupled to the UI, and co-locating it in the same codebase gives you real deployment and iteration advantages.

But the moment you’re dealing with service-to-service authentication, inter-service trust boundaries, or standalone rate limiting and circuit-breaking strategies, those should live in a separate service. Not because Nitro or Next.js can’t handle it technically, but because these concerns change for entirely different reasons than your UI does. Mixing them makes both harder to modify.

The cost of a standalone BFF is one more service to deploy and operate. What you get in return is a clearly scoped deployment unit, independent testability, and a smaller blast radius when something goes wrong. The framework server layer earns its keep as the thing closest to the UI — not as a boundary-free API gateway.


Frameworks are convenient. Don’t let the convenience write your architecture.

Key Takeaways

  • All three frameworks can run BFF logic in their server layer — that’s a capability, not a recommendation.
  • Browser-facing aggregation is a reasonable fit for the framework server layer; risk-bearing backend logic is not.
  • Blurry boundaries don’t fail immediately — they become the thing nobody can explain six months later.
  • A standalone BFF trades deployment complexity for clear ownership and testability.
  • “The framework can do this” and “the framework should do this” are always separate questions.

Sheng’s take, drafted with Claude · part of the 2026-06-13 blog renovation, paint still drying.