Cross-Origin Resource Sharing lets browsers on other origins call your API. The
cors() middleware sets the Access-Control-* headers and answers preflight
OPTIONS requests for you.
Enabling
Register it in your HTTP kernel (app-wide) or on a route group:
import { cors } from "@shaferllc/keel/core";
// In the kernel — applies to every route
this.use(cors());
// Or scoped to an API group
router.group(() => { /* … */ }).use(cors({ origin: ["https://app.example.com"] }));
With no options, cors() reflects the caller's origin — convenient in
development. Lock it down in production with an explicit allowlist.
A production API group
Typical setup for a JSON API served from api.example.com and called from a
SPA on app.example.com:
// app/Http/Kernel.ts
import { cors } from "@shaferllc/keel/core";
this.use(
cors({
origin: ["https://app.example.com"],
credentials: true, // cookies / Authorization
exposeHeaders: ["X-Request-Id"],
}),
);
During local development, allow any localhost port with a predicate:
cors({
origin: (origin) =>
origin.startsWith("http://localhost:") || origin === "https://app.example.com",
credentials: true,
});
Options
cors({
origin: ["https://app.example.com"], // true (reflect) | false | "*" | string[] | (origin, c) => …
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
headers: true, // true (reflect requested) | string[]
exposeHeaders: ["X-Request-Id"], // response headers JS may read
credentials: true, // send Access-Control-Allow-Credentials
maxAge: 86400, // preflight cache seconds; null to omit
});
origin—truereflects the request origin,falseblocks everything,"*"allows any, an array is an allowlist, and a(origin, c) => …predicate returnstrue/false/a specific origin for dynamic decisions (e.g. anylocalhostport in dev).credentials— when on, the spec forbids"*", socors()automatically reflects the concrete origin and addsVary: Origin.headers—trueechoes whatever the browser asks for in the preflight; an array pins an explicit allowlist.
Preflight
Browsers send an OPTIONS request with Access-Control-Request-Method before
certain cross-origin calls. cors() detects these and responds 204 with the
allow headers directly — your route never runs. Everything else falls through to
your handler with the CORS response headers attached.