CORS
The CORS plugin handles Cross-Origin Resource Sharing configuration for your API.
Basic Usage
Section titled “Basic Usage”Allow all origins (development only):
import { Shokupan, Cors } from 'shokupan';
const app = new Shokupan();
app.use(Cors());
app.listen();Configuration
Section titled “Configuration”Single Origin
Section titled “Single Origin”app.use(Cors({ origin: 'https://example.com', credentials: true}));Multiple Origins
Section titled “Multiple Origins”app.use(Cors({ origin: ['https://example.com', 'https://app.example.com'], credentials: true}));Dynamic Origin
Section titled “Dynamic Origin”Validate origins dynamically:
app.use(Cors({ origin: (ctx) => { const origin = ctx.headers.get('origin');
// Allow subdomains of example.com if (origin?.endsWith('.example.com')) { return origin; }
// Allow specific origins const allowedOrigins = [ 'https://example.com', 'https://app.example.com' ];
return allowedOrigins.includes(origin) ? origin : false; }, credentials: true}));Full Options
Section titled “Full Options”app.use(Cors({ // Which origins are allowed origin: '*', // or string, string[], or function
// Which HTTP methods are allowed methods: ['GET', 'POST', 'PUT', 'DELETE'], // or: methods: 'GET,POST,PUT,DELETE'
// Which headers can be sent allowedHeaders: ['Content-Type', 'Authorization'], // or: allowedHeaders: 'Content-Type, Authorization'
// Which headers are exposed to the client exposedHeaders: ['X-Total-Count', 'X-Page-Count'], // or: exposedHeaders: 'X-Total-Count, X-Page-Count'
// Allow credentials (cookies, authorization headers) credentials: true,
// How long preflight requests can be cached (in seconds) maxAge: 86400 // 24 hours}));Common Patterns
Section titled “Common Patterns”API with Authentication
Section titled “API with Authentication”app.use(Cors({ origin: process.env.FRONTEND_URL || 'http://localhost:3000', credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization']}));Public API
Section titled “Public API”app.use(Cors({ origin: '*', methods: ['GET'], credentials: false}));Development vs Production
Section titled “Development vs Production”const isDev = process.env.NODE_ENV !== 'production';
app.use(Cors({ origin: isDev ? '*' : process.env.ALLOWED_ORIGINS?.split(','), credentials: !isDev, methods: ['GET', 'POST', 'PUT', 'DELETE']}));Preflight Requests
Section titled “Preflight Requests”The CORS plugin automatically handles OPTIONS preflight requests:
Client Server │ │ │──OPTIONS /api/users──→│ │ (preflight) │ │ │ │←──200 OK──────────────│ │ Access-Control-* │ │ │ │──POST /api/users────→│ │ (actual request) │ │ │ │←──201 Created─────────│ │ │Per-Route CORS
Section titled “Per-Route CORS”Apply CORS to specific routes:
const corsPublic = Cors({ origin: '*' });const corsPrivate = Cors({ origin: 'https://app.example.com', credentials: true});
// Public endpointapp.get('/api/public', corsPublic, (ctx) => { return { data: 'public' };});
// Private endpointapp.get('/api/private', corsPrivate, (ctx) => { return { data: 'private' };});Troubleshooting
Section titled “Troubleshooting”Credentials and Wildcard
Section titled “Credentials and Wildcard”// ❌ Invalidapp.use(Cors({ origin: '*', credentials: true}));
// ✅ Validapp.use(Cors({ origin: ['https://example.com'], credentials: true}));Headers Not Exposed
Section titled “Headers Not Exposed”If custom headers aren’t visible to the client, add them to exposedHeaders:
app.use(Cors({ origin: 'https://example.com', exposedHeaders: ['X-Custom-Header', 'X-Total-Count']}));Next Steps
Section titled “Next Steps”- Security Headers - Add security headers
- Authentication - Secure your API
- Rate Limiting - Prevent abuse