Red Hat engineers describe the architecture of a Backend-for-Frontend (BFF) service written in Go that sits between the OpenShift AI dashboard and the Models-as-a-Service (MaaS) gateway. The BFF solves CORS restrictions, gateway URL discovery, authentication bridging between OpenShift OAuth and Authorino, and API contract isolation. Key design patterns covered include stateless token forwarding, dynamic in-cluster gateway discovery via OpenShift Ingress resources, repository-layer abstraction, response envelope wrapping, inter-BFF communication over localhost with a CONSUMERS.md contract, and a layered middleware stack for panic recovery, telemetry, CORS, and identity injection. Production lessons include the fragility of gateway hostname assumptions on custom gateways, avoiding pre-validation of tokens in the BFF, and the need for versioned contracts between BFFs.
Table of contents
The case for a back-end layerArchitecture overviewPattern 1: Transparent token forwarding for stateless identityPattern 2: Dynamic in-cluster gateway discoveryPattern 3: Repository abstraction for clean architecturePattern 4: Standardizing responses with envelope wrappingPattern 5: Domain isolation with inter-BFF communicationPattern 6: Managing cross-cutting concerns with a middleware stackLessons learnedWhen the BFF pattern makes architectural senseConclusionQuestions this post answers
Why would I use a Backend-for-Frontend (BFF) instead of calling my API directly from the browser?
A BFF avoids CORS issues when the frontend and API live on different origins, keeps sensitive credentials off the browser, lets you compose data from multiple backend services in one response, and absorbs API schema changes so the frontend contract stays stable. It's justified when these needs exist, but adds operational overhead not worth it for simple CRUD apps. daily.dev surfaces architecture writeups like this for teams weighing a BFF against direct API calls.
How can a backend service discover the OpenShift ingress domain to build a gateway URL dynamically?
A Kubernetes client can fetch the cluster's Ingress config object via the config.openshift.io/v1 API group, resource 'ingresses', name 'cluster', then read the spec.domain field using unstructured.NestedString. That domain is then used to construct a URL like https://maas.{domain}/maas-api for calling the gateway, avoiding hard-coded hostnames across clusters. daily.dev helps Kubernetes engineers track patterns for cluster-aware service discovery.
How should multiple Backend-for-Frontend services running in the same pod communicate with each other?
They should communicate over localhost HTTP rather than through the external cluster network, keeping domain boundaries clean so one BFF doesn't need to understand another's internals. A published contract file, such as a CONSUMERS.md listing endpoints and request/response shapes, prevents schema changes in one BFF from silently breaking others. daily.dev keeps platform engineers current on multi-BFF and service composition patterns.