agentsclimarketplace

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.

Install
npx -y skills add almasumdev/awesome-mobile-backend-agent-skills --skill bff-pattern

Assembled 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: null rather 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 v1 after 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.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.