Skip to content

Frontend Integration Guidelines

CLI tools and libraries have no browser-facing surface. Apply the points below to services that a single-page application (SPA) or other browser-based frontend calls directly.

Why CORS Is Structural, Not Optional

A SPA and its API almost always live on different origins (different port, subdomain, or domain) once deployed for real. Without CORS configured, browsers block every request from the frontend. This isn't a security feature to work around — it's the deployment topology the API has to support.

Configurable Allowed Origin

  • Never default to Access-Control-Allow-Origin: *. A wildcard turns any later oversight (e.g. a service that adds cookie-based auth) into a cross-origin data leak.
  • Never reflect the request's Origin header back verbatim. That is functionally a wildcard, just harder to spot in review.
  • Read the allowed origin from an environment variable at startup (e.g. CORS_ALLOWED_ORIGIN), falling back to a local-development default (http://localhost:3000). Configuration belongs in the environment, never baked into the image (see ci_cd.md).

The Preflight Trap (C++)

cpp-httplib dispatches routes by exact path and method. A per-route Options handler is easy to forget on the next endpoint, and a route registered only with Get never sees the browser's preflight request at all. Use set_pre_routing_handler instead (see the generated src/app/cors.hpp/cors.cpp): it runs before path/method routing, so one registration answers preflight for every current and future path — no per-route opt-in needed. Go's and Java's HTTP stacks don't have this trap: their middleware/filter chains already wrap every request before route matching.

Directives

  • Never set Access-Control-Allow-Origin to * or to the reflected request Origin; use a fixed, configured origin
  • Read the allowed origin from an environment variable with a local-development default; never hardcode it
  • In cpp-httplib, answer OPTIONS preflight in the pre-routing handler, not a per-route handler; it runs before routing and covers every path