Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Application integration

Compose the BFF API and embedded SPA under one guarded HTTP surface while preserving same-origin client behavior.

developer

Compose API and SPA

compose.Handler mounts the BFF mux at /api/ and the SPA handler at /. The mux already registers full /api/... patterns, so the composition is a direct mount and does not strip the prefix a second time. API paths reach the BFF; root and arbitrary client routes reach the SPA.

func Handler(mux http.Handler, spa http.Handler, guard *bff.HostOriginGuard) http.Handler {
  top := http.NewServeMux()
  top.Handle("/api/", mux)
  top.Handle("/", spa)
  return guard.Wrap(top)
}

The SPA can therefore call /api/v1/... through createBFFClient without CORS or a separate origin. The Static bundle and Go webui package provide the two handlers being composed.

Guard the whole surface

The same HostOriginGuard instance wraps the complete top-level handler, including the SPA path. A rebound host is rejected before either the API mux or the embedded shell runs. Reuse the guard passed to the BFF build so allowed-host configuration cannot diverge between API and browser responses.

request
  -> HostOriginGuard
  -> /api/       -> BFF mux -> harness
  -> everything  -> embedded SPA handler

This guard covers the shell as well as API requests. That keeps future HTML additions behind the same origin policy as the control plane. The adjacent Harness guide describes the server surface the BFF forwards to.

Source

The top-level route composition and whole-surface guard are implemented in internal/compose/handler.go. Routing and rebound-host behavior are covered in internal/compose/handler_test.go.

Proof

The integration tests assert /api/v1/sessions and /api/ reach the API handler, root and arbitrary session routes reach the SPA, and disallowed hosts are rejected for both branches before either child handler is called.

← back to documentation