Skip to main content

ADR-004: Backend-for-Frontend API Proxy

Status

Accepted

Context

The application needs to communicate with backend APIs (PublicAPI) while:

  1. Keeping tokens secure: Access tokens must not be exposed to JavaScript
  2. Avoiding CORS issues: Same-origin requests simplify configuration
  3. Centralizing auth logic: Single place for token injection
  4. Supporting anonymous users: Fallback to client credentials

Options considered:

  • Direct API calls: Simple but exposes tokens, CORS complexity
  • API Gateway: Additional infrastructure, latency
  • BFF Proxy: Server-side token injection, same-origin requests

Decision

We will implement a Backend-for-Frontend (BFF) proxy pattern:

  1. Catch-all route at /api/eai/[[...rest]]/route.ts
  2. Token injection: Add Authorization header server-side
  3. Credential forwarding: credentials: 'include' sends session cookies
  4. Anonymous fallback: Client credentials when no user session
// src/app/api/eai/[[...rest]]/route.ts
export async function GET(request: Request, { params }: Props) {
const path = params.rest?.join('/') || '';
const token = await getAccessToken(); // User or client credentials

return fetch(`${process.env.BASE_URL_PUBLIC_API}/${path}`, {
headers: {
Authorization: `Bearer ${token}`,
...forwardedHeaders(request),
},
});
}

Token Priority

The proxy tries token sources in order:

  1. User session token: From Auth.js session (logged-in user)
  2. Client credentials token: Machine-to-machine token (anonymous)

Client Usage

// Client code - simple, no token handling
const response = await fetch('/api/eai/v3/projects', {
credentials: 'include', // Send session cookie
});

Consequences

Positive

  • Security: Tokens never in browser JavaScript
  • Simplicity: Client code doesn't manage tokens
  • CORS-free: Same-origin requests
  • Centralized logging: All API calls pass through proxy

Negative

  • Latency: Extra hop through Next.js server
  • Deployment coupling: API routes must be deployed with frontend
  • Streaming complexity: SSE/WebSocket require special handling

Neutral

  • Error handling: Proxy can normalize error responses
  • Rate limiting: Can be applied at proxy layer