Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/document-cors-headers-behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/platform": patch
---

Document subtle CORS middleware `allowedHeaders` behavior: when empty array (default), it reflects back the client's `Access-Control-Request-Headers` (permissive), and when non-empty array, it only allows specified headers (restrictive). Added comprehensive JSDoc with examples.
50 changes: 50 additions & 0 deletions packages/platform/src/HttpMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,56 @@ export const searchParamsParser: <E, R>(
> = internal.searchParamsParser

/**
* Creates a CORS (Cross-Origin Resource Sharing) middleware for HTTP applications.
*
* @param options - CORS configuration options
* @param options.allowedOrigins - Origins allowed to access the resource. Can be:
* - An array of origin strings (e.g., `["https://example.com", "https://api.example.com"]`)
* - A predicate function to dynamically allow origins
* - If empty array (default): allows all origins with `Access-Control-Allow-Origin: *`
* @param options.allowedMethods - HTTP methods allowed for CORS requests.
* Default: `["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"]`
* @param options.allowedHeaders - Headers allowed in CORS requests. **Important behavior**:
* - If empty array (default): reflects back the client's `Access-Control-Request-Headers`,
* effectively allowing all headers requested by the client
* - If non-empty array: only the specified headers are allowed
* - This means the default behavior is permissive, not restrictive
* @param options.exposedHeaders - Headers exposed to the client in the response.
* Default: `[]`
* @param options.maxAge - Maximum time (in seconds) that preflight request results can be cached.
* If not specified, no `Access-Control-Max-Age` header is sent
* @param options.credentials - Whether to allow credentials (cookies, authorization headers, etc.).
* Default: `false`
*
* @example
* ```ts
* import { HttpMiddleware, HttpRouter, HttpServerResponse } from "@effect/platform"
*
* // Allow all origins and reflect requested headers (default behavior)
* HttpRouter.empty.pipe(
* HttpRouter.get("/", HttpServerResponse.empty()),
* HttpMiddleware.cors()
* )
*
* // Restrict to specific origins and headers
* HttpRouter.empty.pipe(
* HttpRouter.get("/", HttpServerResponse.empty()),
* HttpMiddleware.cors({
* allowedOrigins: ["https://example.com"],
* allowedHeaders: ["Content-Type", "Authorization"],
* credentials: true
* })
* )
*
* // Dynamic origin checking with predicate
* HttpRouter.empty.pipe(
* HttpRouter.get("/", HttpServerResponse.empty()),
* HttpMiddleware.cors({
* allowedOrigins: (origin) => origin.endsWith(".example.com")
* })
* )
* ```
*
* @since 1.0.0
* @category constructors
*/
Expand Down