Handling CORS with Kaito
Kaito does not include any CORS handling out of the box. This is by design to keep the library lightweight and unopinionated. You can easily implement CORS handling in your server by using the before & transform
lifecycle methods.
Manual Implementation (Recommended)
const ALLOWED_ORIGINS = ['http://localhost:3000', 'https://app.example.com'];
export const router = create({
getContext,
before: async req => {
if (req.method === 'OPTIONS') {
return new Response(null, {status: 204}); // Return early to skip the router. This response will be passed to `.transform()`
}
},
transform: async (request, response) => {
const origin = request.headers.get('origin');
// Include CORS headers if the origin is allowed
if (origin && ALLOWED_ORIGINS.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
response.headers.set('Access-Control-Max-Age', '86400');
response.headers.set('Access-Control-Allow-Credentials', 'true');
}
},
});
Experimental CORS API
Kaito also provides an experimental CORS API that can simplify the setup. ⚠️ This API is experimental and may change or be removed at any time. We recommend using the manual implementation above for production applications.
import {create} from '@kaito-http/core';
import {experimental_createCORSTransform} from '@kaito-http/core/cors';
// The origin matcher supports exact matches and protocol-specific wildcard subdomains
const ALLOWED_ORIGINS = [
// Exact matches - protocol required
'https://example.com', // matches only https://example.com
'http://localhost:3000', // matches only http://localhost:3000
// Wildcard subdomain matches - protocol required
'https://*.myapp.com', // matches https://dashboard.myapp.com
// does NOT match https://myapp.com or http://dashboard.myapp.com
// To match both HTTP and HTTPS, include both
'https://*.staging.com', // matches https://app.staging.com
'http://*.staging.com', // matches http://app.staging.com
// To match both subdomain and root domain, include both
'https://*.production.com', // matches https://app.production.com
'https://production.com', // matches https://production.com
];
const cors = experimental_createCORSTransform(ALLOWED_ORIGINS);
export const router = create({
getContext,
before: async req => {
if (req.method === 'OPTIONS') {
// Return early to skip the router
// This response will still be passed to `.transform()`
// so our CORS headers will be applied
return new Response(null, {status: 204});
}
},
transform: async (request, response) => {
cors(request, response);
},
});
The experimental CORS API includes a powerful origin matcher that supports both exact matches and wildcard subdomains. Important rules to note:
- Protocol (http:// or https://) must be specified for all patterns
- Wildcard patterns (e.g.,
https://*.example.com
) will ONLY match subdomains, NOT the root domain - To match both HTTP and HTTPS for a domain, include both patterns
- To match both subdomains and root domain, include both patterns
Examples:
https://example.com
- Matches exactly this origin with HTTPShttps://*.example.com
- Matches HTTPS subdomains likehttps://app.example.com
, but NOThttps://example.com
orhttp://app.example.com
http://localhost:3000
- Matches exactly this origin with HTTP