Bff pattern
Skill almasumdev/awesome-mobile-backend-agent-skills/.github/skills/api/bff-pattern
Agent skills for the backend-for-mobile layer: APIs, auth, push, sync, and BaaS integrations.
npx -y skills add almasumdev/awesome-mobile-backend-agent-skills --skill bff-patternAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
When and how to use a Backend-for-Frontend (BFF) for mobile -- scoping, ownership, and anti-patterns. Use when deciding whether a BFF is justified or designing one.
SKILL.md
4.8 KB, as published. Nobody here has run it
Backend-for-Frontend (BFF) Pattern
Instructions
A BFF is a thin service dedicated to one client class (here, the mobile app). It composes calls to internal microservices and shapes payloads to match the app's screens. A BFF is a force multiplier when internal services are generic and the app makes many round-trips; it becomes a liability if it hides business rules or duplicates them.
1. Rationale
Introduce a mobile BFF when two or more of these are true:
- A single screen requires 3+ calls to internal services that could be composed server-side.
- Internal service payloads carry fields the mobile app should never see (PII, internal flags).
- The mobile team needs to move faster than internal service teams (different release cadence).
- Different client classes (web, mobile, TV) need meaningfully different payloads from the same backend truth.
Do not introduce a BFF if the underlying services already return screen-appropriate data, or if the BFF would merely proxy without composition.
2. Scoping
One BFF per client class. Naming: mobile-bff, web-bff. Avoid a single "omni-bff" -- it re-creates the coupling you left behind.
┌─────────────┐
Mobile -> │ mobile-bff │ -> users-svc, feed-svc, search-svc, media-svc
└─────────────┘
┌─────────────┐
Web -> │ web-bff │ -> users-svc, feed-svc, search-svc
└─────────────┘
3. Ownership
The mobile team owns mobile-bff. This is non-negotiable; otherwise it becomes another backend team queue.
- Same repo as the app is acceptable for small teams; separate repo + shared CI at scale.
- Release train coupled to the mobile app (or slightly ahead -- the BFF ships first so app rollout can fall back to a field flag).
4. What Belongs in a BFF
- Composition / fan-out to internal services.
- Payload shaping (field selection, renaming, flattening for the screen).
- Client-specific auth glue (exchanging an app-issued session for internal service tokens).
- Feature flag evaluation for the client.
- Per-screen caching (short TTL, edge-friendly).
5. What Does Not Belong
- Business rules (pricing, permissions, workflow state) -- those live in the domain services.
- Data ownership -- the BFF has no database of its own beyond a cache.
- Cross-client logic -- if two BFFs need the same rule, push it down.
6. Example Endpoint
A home screen that currently needs: /users/me, /feed?user=me, /stories/live, /notifications/unread.
BFF endpoint:
GET /mobile/v1/home
Authorization: Bearer ...
Kotlin (Ktor) composition:
suspend fun ApplicationCall.home() {
val userId = principal<UserPrincipal>()!!.id
coroutineScope {
val user = async { users.me(userId) }
val feed = async { feed.top(userId, limit = 20) }
val live = async { stories.live(userId) }
val unread = async { notifications.unread(userId) }
respond(HomePayload(user.await(), feed.await(), live.await(), unread.await()))
}
}
Client consumption (Swift):
struct Home: Decodable { let user: User; let feed: [Article]; let live: [Story]; let unread: Int }
let home: Home = try await api.get("/mobile/v1/home")
One call, shaped exactly for the screen. The underlying four services remain untouched and generic.
7. Reliability Patterns
- Timeouts: per downstream, aggressive (e.g., 300 ms). The BFF assembles a partial response if a non-critical service is slow.
- Fallbacks: return the screen with
unread: nullrather than failing the whole call. - Bulkheads: separate connection pools per downstream so one slow service cannot starve the others.
- Circuit breakers: open on repeated downstream failure; return cached/empty shapes.
8. Caching
Short, per-user TTLs (seconds, not minutes) keyed by (endpoint, user_id, feature_flags). Invalidation on mutation is usually not worth the complexity.
9. Versioning
Because the BFF serves one client, version it in lockstep with the app:
/mobile/v1/*freezes when/mobile/v2/*ships.- Retire
v1after the minimum-supported app version drops below that release.
Checklist
- Clear justification for the BFF (composition / shaping / client velocity).
- One BFF per client class; mobile team owns
mobile-bff. - No business rules or data ownership in the BFF.
- Per-downstream timeouts, bulkheads, and fallbacks configured.
- Endpoints versioned in lockstep with the mobile app.
- Per-user cache TTLs documented and bounded.
- Auth exchange (app session → internal tokens) isolated in one module.