flowCreate.solutions

Generic Example: BFF Route Handler Proxy

This example shows the standard BFF responsibilities in one place:

  • CSRF validation for unsafe methods
  • read access token from HttpOnly cookie (server-side)
  • call backend via server-only BACKEND_BASE_URL
  • attach Authorization: Bearer ...
// app/api/v1/things/route.ts
import { cookies, headers } from "next/headers";

function assertSameOrigin(origin: string | null, referer: string | null) {
  // Project-specific; keep strict. Reject when missing for unsafe methods if needed.
}

function validateCsrf(req: Request) {
  if (!["POST", "PUT", "PATCH", "DELETE"].includes(req.method)) return;

  const origin = req.headers.get("origin");
  const referer = req.headers.get("referer");
  assertSameOrigin(origin, referer);

  const csrfCookie = cookies().get("csrf_token")?.value;
  const csrfHeader = req.headers.get("x-csrf-token");
  if (!csrfCookie || !csrfHeader || csrfCookie !== csrfHeader) {
    throw new Error("CSRF validation failed");
  }
}

export async function POST(req: Request) {
  validateCsrf(req);

  const accessJwt = cookies().get("access_token")?.value;
  if (!accessJwt) return Response.json({ success: false, title: "Unauthorized" }, { status: 401 });

  const backendBaseUrl = process.env.BACKEND_BASE_URL;
  if (!backendBaseUrl) return Response.json({ success: false, title: "Misconfigured" }, { status: 500 });

  const requestId = headers().get("x-request-id") || undefined;

  const upstream = await fetch(`${backendBaseUrl}/api/v1/things`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      authorization: `Bearer ${accessJwt}`,
      ...(requestId ? { "x-request-id": requestId } : {}),
    },
    body: await req.text(),
  });

  const body = await upstream.json().catch(() => ({}));
  return Response.json(body, { status: upstream.status });
}