Jonatan Matajonmatum.com
conceptsnotesexperimentsessays
© 2026 Jonatan Mata. All rights reserved.v2.1.1
Concepts

Backend for Frontend

Architectural pattern where each client type has its own dedicated backend adapting microservice APIs to that client's specific needs.

evergreen#bff#pattern#frontend#api#microservices#architecture#graphql

What it is

Backend for Frontend (BFF) is an architectural pattern where each client type — web, mobile, IoT, desktop applications — has its own dedicated backend. Instead of forcing all clients to consume the same generic APIs, each BFF acts as an adaptation layer that aggregates, transforms, and optimizes data specifically for its client's needs.

The pattern emerges from the practical reality that different clients have different data access patterns, network constraints, processing capabilities, and user experiences. A web application might need rich, detailed data, while a mobile app requires lighter responses optimized for slow connections. An IoT device might need only a minimal subset of data in binary format.

Each BFF is owned by the corresponding frontend team, enabling greater autonomy and development velocity. This contrasts with a centralized API Gateway that attempts to serve all clients with a single interface.

Implementation patterns

BFF per platform

The most common pattern is creating a dedicated BFF for each client platform:

// Web BFF - Optimized for web applications
app.get('/api/dashboard', async (req, res) => {
  const [user, analytics, notifications, settings] = await Promise.all([
    userService.getProfile(req.userId),
    analyticsService.getDashboardData(req.userId),
    notificationService.getRecent(req.userId, 10),
    settingsService.getUserPreferences(req.userId)
  ]);
 
  // Rich aggregation for web with multiple widgets
  res.json({
    user: {
      name: user.fullName,
      avatar: user.profileImage,
      role: user.role
    },
    dashboard: {
      metrics: analytics.summary,
      charts: analytics.chartData,
      notifications: notifications.map(n => ({
        id: n.id,
        message: n.content,
        timestamp: n.createdAt,
        priority: n.priority
      })),
      quickActions: settings.enabledActions
    }
  });
});
 
// Mobile BFF - Optimized for mobile devices
app.get('/api/dashboard', async (req, res) => {
  const [user, criticalNotifications] = await Promise.all([
    userService.getProfile(req.userId),
    notificationService.getCritical(req.userId, 3)
  ]);
 
  // Lightweight response for mobile
  res.json({
    user: user.displayName,
    avatar: user.thumbnailImage,
    alerts: criticalNotifications.length,
    hasUpdates: criticalNotifications.length > 0
  });
});

BFF with GraphQL

GraphQL can act as a natural BFF, allowing each client to request exactly the data it needs:

# GraphQL schema as BFF
type Query {
  dashboard(platform: Platform!): DashboardData
}
 
type DashboardData {
  user: User
  metrics: [Metric]
  notifications: [Notification]
  settings: UserSettings
}
 
enum Platform {
  WEB
  MOBILE
  TABLET
}
// Resolver that adapts based on platform
const resolvers = {
  Query: {
    dashboard: async (_, { platform }, { userId }) => {
      const baseData = await getDashboardData(userId);
      
      switch (platform) {
        case 'WEB':
          return {
            ...baseData,
            metrics: baseData.metrics, // Complete data
            notifications: baseData.notifications.slice(0, 10)
          };
        case 'MOBILE':
          return {
            user: baseData.user,
            metrics: baseData.metrics.filter(m => m.priority === 'HIGH'),
            notifications: baseData.notifications.slice(0, 3)
          };
        default:
          return baseData;
      }
    }
  }
};

When to use BFF

Ideal scenarios

  • Multiple client types with significantly different data needs
  • Autonomous frontend teams that need to iterate quickly without depending on centralized API changes
  • Granular microservice APIs that require multiple calls to complete a client operation
  • Platform-specific optimizations like image compression, data formats, or communication protocols

When to avoid BFF

The BFF pattern adds unnecessary complexity in certain scenarios:

  • Single client type — a generic API Gateway is sufficient
  • Small teams where maintaining multiple BFFs exceeds development capacity
  • Minimal differences between clients — only response format or optional fields
  • Already optimized APIs that provide exactly what each client needs

Pattern comparison

AspectBFFAPI GatewayGraphQL
PurposeAdapt data per clientRoute and protectFlexible querying
OwnershipFrontend teamPlatform teamShared
Business logicAggregation and transformationRouting and authData resolution
QuantityOne per client typeOne centralizedOne with multiple queries
ComplexityMedium-HighLow-MediumMedium
FlexibilityHigh per clientLowVery high
Network overheadOptimizedVariableOptimized

Common anti-patterns

BFF as simple proxy

// ❌ Anti-pattern: BFF that only forwards calls
app.get('/api/users/:id', (req, res) => {
  // Just proxy, no added value
  return userService.getUser(req.params.id);
});
// ✅ Correct pattern: BFF that adds value
app.get('/api/users/:id', async (req, res) => {
  const [user, permissions, preferences] = await Promise.all([
    userService.getUser(req.params.id),
    authService.getUserPermissions(req.params.id),
    settingsService.getPreferences(req.params.id)
  ]);
 
  return {
    profile: {
      name: user.fullName,
      email: user.email,
      avatar: user.profileImage
    },
    capabilities: permissions.map(p => p.action),
    settings: preferences.ui
  };
});

Business logic in BFF

BFFs should be limited to data aggregation and transformation, not implement complex business logic that should reside in the underlying microservices.

Architecture integration

A BFF can coexist with other architectural patterns:

  • With API Gateway: The gateway handles authentication, rate limiting, and routing, while the BFF focuses on data aggregation
  • With Event-Driven Architecture: BFFs can subscribe to events to maintain local caches or notify clients of changes
  • With Micro-frontends: Each micro-frontend can have its own BFF, maintaining team autonomy

Why it matters

The BFF pattern solves the fundamental problem of generic APIs trying to serve multiple clients with divergent needs. In organizations with autonomous frontend teams, it enables each team to optimize their backend for specific access patterns without impacting other clients. This reduces latency, improves user experience, and accelerates development by eliminating dependencies between teams. However, it introduces operational complexity — each BFF must be deployed, monitored, and maintained independently, requiring careful consideration of the tradeoff between team autonomy and infrastructure overhead.

References

  • Pattern: Backends for Frontends — Sam Newman. The original article defining the BFF pattern.
  • Backends for Frontends pattern — Microsoft, 2023. Implementation guide for Azure.
  • The Back-end for Front-end Pattern (BFF) — Phil Calçado, 2015. Detailed pattern analysis with use cases.
  • Building Microservices — Sam Newman, O'Reilly, 2021. Chapter on BFF in microservices context.
  • Thinking in Graphs — GraphQL Foundation. Fundamental GraphQL concepts as BFF.
  • Micro Frontends — Martin Fowler, 2019. BFF integration with micro-frontend architectures.

Related content

  • Microservices

    Architectural style structuring an application as a collection of small, independent, deployable services, each with its own business logic and data.

  • API Design

    Principles and practices for designing clear, consistent, and evolvable programming interfaces that facilitate integration between systems.

  • API Gateway Pattern

    Pattern providing a single entry point for multiple microservices, handling routing, authentication, rate limiting, and response aggregation.

  • Micro Frontends

    Architectural pattern extending microservices to the frontend, allowing independent teams to develop and deploy parts of a web application autonomously.

  • Event-Driven Architecture

    Architectural pattern where components communicate through asynchronous events, enabling decoupled, scalable, and reactive systems.

Concepts