API gateway and BFF governance
This document encodes the hybrid rule used across PREGO web apps: do not treat “Zuplo only” as dogma; do treat the API gateway (Zuplo) as the single entry point for external, multi-consumer, and operations-facing HTTP APIs.
Principles
- Gateway first — New public contracts live in prego-zuplo OpenAPI, then routes, then Workers / Frappe / auth upstream. See How to add an API.
- Thin BFF — Same-origin
app/api/*(Next.js) or Cloudflare Pages Functions are allowed only when the gateway cannot reasonably satisfy host-aligned cookies, server-only secrets, or same-origin-only policy. BFFs proxy; they do not own business rules. - Short exception list — Large file presigned upload URLs (e.g. R2) and static CDN assets are intentional exceptions. Document them in PRs when adding new patterns.
- No browser →
/internal/*— Internal APIs use Bearer and must not be called from the browser.
By app (current posture)
| App | Gateway (Zuplo) | BFF / edge |
|---|---|---|
| www | /trial and /trial/manual-approval/* → browser POST /api/trial/* on Zuplo via NEXT_PUBLIC_ZUPLO_API_URL (email check, manual approval draft / work-email OTP / submit-details — no trial phone SMS routes; no www BFF) | Pages Functions for /api/country (and similar non-API static edge needs) |
| admin-web | NEXT_PUBLIC_PREGO_CONTROL_PLANE_URL, NEXT_PUBLIC_PREGO_AUTH_URL (Zuplo bases) | Same-origin /api/auth/*, /api/billing/* (subscription, payments, usage → CP /internal/billing/*), /api/trial/onboarding-handoff-verify (cookies, internal Bearer) |
| client-web | NEXT_PUBLIC_API_URL for tenant and auth paths | Same-origin routes for static-ish content (e.g. onboarding markdown); Assistant UI uses presigned upload_url (exception) |
Related
- Tenant and trust boundaries — optional
x-trace-id, header rules. - prego-zuplo runbook — CORS /
ALLOWED_ORIGINS— required when browsers call Zuplo cross-origin.
한국어 요약
대외 API는 Zuplo를 표면으로 두고, 쿠키·비밀·대용량 업로드만 얇은 BFF 또는 presigned 직통으로 예외를 짧게 유지합니다. www 트라이얼·수동 승인(이메일 OTP·제출 포함)도 브라우저가 Zuplo로만 호출합니다. 내부 API는 브라우저에서 호출하지 않습니다.