balda-js 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cli.d.ts +6 -0
- package/lib/cli.js +929 -0
- package/lib/cli.js.map +1 -0
- package/lib/index.cjs +3384 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +1492 -0
- package/lib/index.d.ts +1492 -0
- package/lib/index.js +3327 -0
- package/lib/index.js.map +1 -0
- package/package.json +1 -1
- package/.husky/pre-commit +0 -19
- package/.nvmrc +0 -1
- package/docs/README.md +0 -135
- package/docs/blog/authors.yml +0 -6
- package/docs/blog/tags.yml +0 -4
- package/docs/cli.md +0 -109
- package/docs/docs/core-concepts/controllers.md +0 -393
- package/docs/docs/core-concepts/middleware.md +0 -302
- package/docs/docs/core-concepts/request-response.md +0 -486
- package/docs/docs/core-concepts/routing.md +0 -388
- package/docs/docs/core-concepts/server.md +0 -332
- package/docs/docs/cron/overview.md +0 -70
- package/docs/docs/examples/rest-api.md +0 -595
- package/docs/docs/getting-started/configuration.md +0 -168
- package/docs/docs/getting-started/installation.md +0 -125
- package/docs/docs/getting-started/quick-start.md +0 -273
- package/docs/docs/intro.md +0 -46
- package/docs/docs/plugins/cookie.md +0 -424
- package/docs/docs/plugins/cors.md +0 -295
- package/docs/docs/plugins/file.md +0 -382
- package/docs/docs/plugins/helmet.md +0 -388
- package/docs/docs/plugins/json.md +0 -338
- package/docs/docs/plugins/log.md +0 -592
- package/docs/docs/plugins/overview.md +0 -390
- package/docs/docs/plugins/rate-limiter.md +0 -347
- package/docs/docs/plugins/static.md +0 -352
- package/docs/docs/plugins/swagger.md +0 -411
- package/docs/docs/plugins/urlencoded.md +0 -76
- package/docs/docs/testing/examples.md +0 -384
- package/docs/docs/testing/mock-server.md +0 -311
- package/docs/docs/testing/overview.md +0 -76
- package/docs/docusaurus.config.ts +0 -144
- package/docs/intro.md +0 -78
- package/docs/package.json +0 -46
- package/docs/sidebars.ts +0 -72
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.svg +0 -1
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -37
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docs/tsconfig.json +0 -8
- package/speed_test.sh +0 -3
- package/test/benchmark/index.ts +0 -17
- package/test/cli/cli.ts +0 -7
- package/test/commands/test.ts +0 -42
- package/test/controllers/file_upload.ts +0 -29
- package/test/controllers/urlencoded.ts +0 -13
- package/test/controllers/users.ts +0 -111
- package/test/cron/index.ts +0 -6
- package/test/cron/test_cron.ts +0 -8
- package/test/cron/test_cron_imported.ts +0 -8
- package/test/native_env.ts +0 -16
- package/test/resources/test.txt +0 -1
- package/test/server/index.ts +0 -3
- package/test/server/instance.ts +0 -63
- package/test/suite/upload.test.ts +0 -23
- package/test/suite/urlencoded.test.ts +0 -23
- package/test/suite/users.test.ts +0 -76
- package/todo.md +0 -9
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -17
@@ -1,424 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 5
|
3
|
-
---
|
4
|
-
|
5
|
-
# Cookie Plugin
|
6
|
-
|
7
|
-
The Cookie plugin provides comprehensive cookie parsing and management for your Balda.js application. It automatically parses incoming cookies and provides convenient methods for setting and clearing cookies on responses.
|
8
|
-
|
9
|
-
## Features
|
10
|
-
|
11
|
-
- **Automatic Cookie Parsing**: Parses cookies from incoming requests
|
12
|
-
- **Cookie Setting**: Easy methods to set cookies on responses
|
13
|
-
- **Cookie Signing**: Optional cookie signing for security
|
14
|
-
- **Flexible Configuration**: Customizable defaults and options
|
15
|
-
- **Security Features**: HttpOnly, Secure, SameSite support
|
16
|
-
|
17
|
-
## Basic Configuration
|
18
|
-
|
19
|
-
### Simple Setup
|
20
|
-
|
21
|
-
```typescript
|
22
|
-
import { Server } from 'balda-js';
|
23
|
-
|
24
|
-
const server = new Server({
|
25
|
-
port: 3000,
|
26
|
-
plugins: {
|
27
|
-
cookie: {
|
28
|
-
secret: 'your-secret-key'
|
29
|
-
}
|
30
|
-
}
|
31
|
-
});
|
32
|
-
```
|
33
|
-
|
34
|
-
### Development Configuration
|
35
|
-
|
36
|
-
```typescript
|
37
|
-
const server = new Server({
|
38
|
-
port: 3000,
|
39
|
-
plugins: {
|
40
|
-
cookie: {
|
41
|
-
secret: 'dev-secret-key',
|
42
|
-
defaults: {
|
43
|
-
httpOnly: true,
|
44
|
-
secure: false,
|
45
|
-
sameSite: 'Lax'
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
49
|
-
});
|
50
|
-
```
|
51
|
-
|
52
|
-
## Configuration Options
|
53
|
-
|
54
|
-
### Basic Options
|
55
|
-
|
56
|
-
```typescript
|
57
|
-
cookie: {
|
58
|
-
secret: 'your-secret-key', // Required for signed cookies
|
59
|
-
parse: true, // Enable cookie parsing (default: true)
|
60
|
-
sign: false, // Enable cookie signing (default: false)
|
61
|
-
defaults: {
|
62
|
-
path: '/', // Cookie path (default: '/')
|
63
|
-
httpOnly: true, // HttpOnly flag (default: true)
|
64
|
-
secure: false, // Secure flag (default: false)
|
65
|
-
sameSite: 'Lax' // SameSite attribute (default: 'Lax')
|
66
|
-
}
|
67
|
-
}
|
68
|
-
```
|
69
|
-
|
70
|
-
### Production Configuration
|
71
|
-
|
72
|
-
```typescript
|
73
|
-
const server = new Server({
|
74
|
-
port: 3000,
|
75
|
-
plugins: {
|
76
|
-
cookie: {
|
77
|
-
secret: process.env.COOKIE_SECRET,
|
78
|
-
sign: true,
|
79
|
-
defaults: {
|
80
|
-
httpOnly: true,
|
81
|
-
secure: true, // HTTPS only
|
82
|
-
sameSite: 'Strict',
|
83
|
-
domain: '.myapp.com'
|
84
|
-
}
|
85
|
-
}
|
86
|
-
}
|
87
|
-
});
|
88
|
-
```
|
89
|
-
|
90
|
-
## Usage Examples
|
91
|
-
|
92
|
-
### Basic Cookie Operations
|
93
|
-
|
94
|
-
```typescript
|
95
|
-
@controller('/auth')
|
96
|
-
export class AuthController {
|
97
|
-
@post('/login')
|
98
|
-
async login(req: Request, res: Response) {
|
99
|
-
const { username, password } = req.body;
|
100
|
-
|
101
|
-
// Validate credentials
|
102
|
-
const user = await validateUser(username, password);
|
103
|
-
|
104
|
-
if (user) {
|
105
|
-
// Set session cookie
|
106
|
-
res.cookie('sessionId', user.sessionId, {
|
107
|
-
maxAge: 24 * 60 * 60 * 1000, // 24 hours
|
108
|
-
httpOnly: true,
|
109
|
-
secure: true
|
110
|
-
});
|
111
|
-
|
112
|
-
res.json({ message: 'Login successful' });
|
113
|
-
} else {
|
114
|
-
res.unauthorized({ error: 'Invalid credentials' });
|
115
|
-
}
|
116
|
-
}
|
117
|
-
|
118
|
-
@post('/logout')
|
119
|
-
async logout(req: Request, res: Response) {
|
120
|
-
// Clear session cookie
|
121
|
-
res.clearCookie('sessionId');
|
122
|
-
res.json({ message: 'Logout successful' });
|
123
|
-
}
|
124
|
-
}
|
125
|
-
```
|
126
|
-
|
127
|
-
### Reading Cookies
|
128
|
-
|
129
|
-
```typescript
|
130
|
-
@controller('/api')
|
131
|
-
export class ApiController {
|
132
|
-
@get('/profile')
|
133
|
-
async getProfile(req: Request, res: Response) {
|
134
|
-
// Access parsed cookies
|
135
|
-
const sessionId = req.cookies.sessionId;
|
136
|
-
const theme = req.cookies.theme || 'light';
|
137
|
-
const language = req.cookies.language || 'en';
|
138
|
-
|
139
|
-
if (!sessionId) {
|
140
|
-
return res.unauthorized({ error: 'No session found' });
|
141
|
-
}
|
142
|
-
|
143
|
-
const user = await getUserBySession(sessionId);
|
144
|
-
res.json({ user, theme, language });
|
145
|
-
}
|
146
|
-
}
|
147
|
-
```
|
148
|
-
|
149
|
-
### Setting Multiple Cookies
|
150
|
-
|
151
|
-
```typescript
|
152
|
-
@post('/preferences')
|
153
|
-
async updatePreferences(req: Request, res: Response) {
|
154
|
-
const { theme, language } = req.body;
|
155
|
-
|
156
|
-
// Set multiple cookies
|
157
|
-
res.cookie('theme', theme, {
|
158
|
-
maxAge: 365 * 24 * 60 * 60 * 1000, // 1 year
|
159
|
-
httpOnly: false // Allow JavaScript access
|
160
|
-
});
|
161
|
-
|
162
|
-
res.cookie('language', language, {
|
163
|
-
maxAge: 365 * 24 * 60 * 60 * 1000,
|
164
|
-
httpOnly: false
|
165
|
-
});
|
166
|
-
|
167
|
-
res.json({ message: 'Preferences updated' });
|
168
|
-
}
|
169
|
-
```
|
170
|
-
|
171
|
-
### Signed Cookies
|
172
|
-
|
173
|
-
```typescript
|
174
|
-
const server = new Server({
|
175
|
-
port: 3000,
|
176
|
-
plugins: {
|
177
|
-
cookie: {
|
178
|
-
secret: 'your-secret-key',
|
179
|
-
sign: true,
|
180
|
-
defaults: {
|
181
|
-
httpOnly: true,
|
182
|
-
secure: true
|
183
|
-
}
|
184
|
-
}
|
185
|
-
}
|
186
|
-
});
|
187
|
-
|
188
|
-
@controller('/secure')
|
189
|
-
export class SecureController {
|
190
|
-
@post('/set-secure-cookie')
|
191
|
-
async setSecureCookie(req: Request, res: Response) {
|
192
|
-
// Set signed cookie
|
193
|
-
res.cookie('secureData', 'sensitive-information', {
|
194
|
-
signed: true,
|
195
|
-
maxAge: 60 * 60 * 1000 // 1 hour
|
196
|
-
});
|
197
|
-
|
198
|
-
res.json({ message: 'Secure cookie set' });
|
199
|
-
}
|
200
|
-
|
201
|
-
@get('/read-secure-cookie')
|
202
|
-
async readSecureCookie(req: Request, res: Response) {
|
203
|
-
// Read signed cookie
|
204
|
-
const secureData = req.cookies.secureData;
|
205
|
-
|
206
|
-
if (secureData) {
|
207
|
-
res.json({ data: secureData });
|
208
|
-
} else {
|
209
|
-
res.json({ error: 'No secure data found' });
|
210
|
-
}
|
211
|
-
}
|
212
|
-
}
|
213
|
-
```
|
214
|
-
|
215
|
-
## Cookie Options
|
216
|
-
|
217
|
-
### Domain Configuration
|
218
|
-
|
219
|
-
```typescript
|
220
|
-
// Set cookie for specific domain
|
221
|
-
res.cookie('sessionId', 'value', {
|
222
|
-
domain: '.myapp.com' // Available on all subdomains
|
223
|
-
});
|
224
|
-
|
225
|
-
// Set cookie for current domain only
|
226
|
-
res.cookie('sessionId', 'value', {
|
227
|
-
domain: 'api.myapp.com' // Only on this subdomain
|
228
|
-
});
|
229
|
-
```
|
230
|
-
|
231
|
-
### Path Configuration
|
232
|
-
|
233
|
-
```typescript
|
234
|
-
// Set cookie for specific path
|
235
|
-
res.cookie('adminToken', 'value', {
|
236
|
-
path: '/admin' // Only available on /admin routes
|
237
|
-
});
|
238
|
-
|
239
|
-
// Set cookie for entire site
|
240
|
-
res.cookie('sessionId', 'value', {
|
241
|
-
path: '/' // Available everywhere
|
242
|
-
});
|
243
|
-
```
|
244
|
-
|
245
|
-
### Expiration Options
|
246
|
-
|
247
|
-
```typescript
|
248
|
-
// Expire at specific date
|
249
|
-
res.cookie('sessionId', 'value', {
|
250
|
-
expires: new Date('2024-12-31')
|
251
|
-
});
|
252
|
-
|
253
|
-
// Expire after specific time
|
254
|
-
res.cookie('sessionId', 'value', {
|
255
|
-
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
256
|
-
});
|
257
|
-
|
258
|
-
// Session cookie (expires when browser closes)
|
259
|
-
res.cookie('sessionId', 'value', {
|
260
|
-
// No expires or maxAge
|
261
|
-
});
|
262
|
-
```
|
263
|
-
|
264
|
-
### Security Options
|
265
|
-
|
266
|
-
```typescript
|
267
|
-
// Secure cookie (HTTPS only)
|
268
|
-
res.cookie('sessionId', 'value', {
|
269
|
-
secure: true,
|
270
|
-
httpOnly: true,
|
271
|
-
sameSite: 'Strict'
|
272
|
-
});
|
273
|
-
|
274
|
-
// Cross-site cookie
|
275
|
-
res.cookie('trackingId', 'value', {
|
276
|
-
secure: true,
|
277
|
-
sameSite: 'None' // Requires secure: true
|
278
|
-
});
|
279
|
-
```
|
280
|
-
|
281
|
-
## SameSite Configuration
|
282
|
-
|
283
|
-
### SameSite Options
|
284
|
-
|
285
|
-
```typescript
|
286
|
-
// Strict - only same site
|
287
|
-
res.cookie('sessionId', 'value', {
|
288
|
-
sameSite: 'Strict'
|
289
|
-
});
|
290
|
-
|
291
|
-
// Lax - same site + top-level navigation
|
292
|
-
res.cookie('sessionId', 'value', {
|
293
|
-
sameSite: 'Lax'
|
294
|
-
});
|
295
|
-
|
296
|
-
// None - cross-site (requires secure: true)
|
297
|
-
res.cookie('trackingId', 'value', {
|
298
|
-
sameSite: 'None',
|
299
|
-
secure: true
|
300
|
-
});
|
301
|
-
```
|
302
|
-
|
303
|
-
## Environment-Based Configuration
|
304
|
-
|
305
|
-
### Development vs Production
|
306
|
-
|
307
|
-
```typescript
|
308
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
309
|
-
|
310
|
-
const server = new Server({
|
311
|
-
port: 3000,
|
312
|
-
plugins: {
|
313
|
-
cookie: {
|
314
|
-
secret: process.env.COOKIE_SECRET,
|
315
|
-
sign: isProduction,
|
316
|
-
defaults: {
|
317
|
-
httpOnly: true,
|
318
|
-
secure: isProduction, // HTTPS only in production
|
319
|
-
sameSite: isProduction ? 'Strict' : 'Lax',
|
320
|
-
domain: isProduction ? '.myapp.com' : undefined
|
321
|
-
}
|
322
|
-
}
|
323
|
-
}
|
324
|
-
});
|
325
|
-
```
|
326
|
-
|
327
|
-
## Error Handling
|
328
|
-
|
329
|
-
### Invalid Signed Cookies
|
330
|
-
|
331
|
-
```typescript
|
332
|
-
@get('/verify-cookie')
|
333
|
-
async verifyCookie(req: Request, res: Response) {
|
334
|
-
const signedCookie = req.cookies.secureData;
|
335
|
-
|
336
|
-
if (!signedCookie) {
|
337
|
-
return res.unauthorized({ error: 'No signed cookie found' });
|
338
|
-
}
|
339
|
-
|
340
|
-
// The cookie plugin automatically verifies signed cookies
|
341
|
-
// If verification fails, the cookie won't be available
|
342
|
-
res.json({ verified: true, data: signedCookie });
|
343
|
-
}
|
344
|
-
```
|
345
|
-
|
346
|
-
## Integration Examples
|
347
|
-
|
348
|
-
### With Authentication
|
349
|
-
|
350
|
-
```typescript
|
351
|
-
@controller('/auth')
|
352
|
-
export class AuthController {
|
353
|
-
@post('/login')
|
354
|
-
async login(req: Request, res: Response) {
|
355
|
-
const { username, password } = req.body;
|
356
|
-
|
357
|
-
const user = await authenticateUser(username, password);
|
358
|
-
|
359
|
-
if (user) {
|
360
|
-
// Set authentication cookies
|
361
|
-
res.cookie('authToken', user.token, {
|
362
|
-
httpOnly: true,
|
363
|
-
secure: true,
|
364
|
-
sameSite: 'Strict',
|
365
|
-
maxAge: 24 * 60 * 60 * 1000
|
366
|
-
});
|
367
|
-
|
368
|
-
res.cookie('userId', user.id.toString(), {
|
369
|
-
httpOnly: true,
|
370
|
-
secure: true,
|
371
|
-
sameSite: 'Strict',
|
372
|
-
maxAge: 24 * 60 * 60 * 1000
|
373
|
-
});
|
374
|
-
|
375
|
-
res.json({ message: 'Login successful' });
|
376
|
-
} else {
|
377
|
-
res.unauthorized({ error: 'Invalid credentials' });
|
378
|
-
}
|
379
|
-
}
|
380
|
-
|
381
|
-
@post('/logout')
|
382
|
-
async logout(req: Request, res: Response) {
|
383
|
-
// Clear all authentication cookies
|
384
|
-
res.clearCookie('authToken');
|
385
|
-
res.clearCookie('userId');
|
386
|
-
|
387
|
-
res.json({ message: 'Logout successful' });
|
388
|
-
}
|
389
|
-
}
|
390
|
-
```
|
391
|
-
|
392
|
-
### With Session Management
|
393
|
-
|
394
|
-
```typescript
|
395
|
-
@controller('/session')
|
396
|
-
export class SessionController {
|
397
|
-
@post('/create')
|
398
|
-
async createSession(req: Request, res: Response) {
|
399
|
-
const sessionId = generateSessionId();
|
400
|
-
|
401
|
-
res.cookie('sessionId', sessionId, {
|
402
|
-
httpOnly: true,
|
403
|
-
secure: true,
|
404
|
-
sameSite: 'Strict',
|
405
|
-
maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
|
406
|
-
});
|
407
|
-
|
408
|
-
await createSession(sessionId, req.body);
|
409
|
-
res.json({ sessionId });
|
410
|
-
}
|
411
|
-
|
412
|
-
@get('/data')
|
413
|
-
async getSessionData(req: Request, res: Response) {
|
414
|
-
const sessionId = req.cookies.sessionId;
|
415
|
-
|
416
|
-
if (!sessionId) {
|
417
|
-
return res.unauthorized({ error: 'No session found' });
|
418
|
-
}
|
419
|
-
|
420
|
-
const sessionData = await getSession(sessionId);
|
421
|
-
res.json(sessionData);
|
422
|
-
}
|
423
|
-
}
|
424
|
-
```
|
@@ -1,295 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 2
|
3
|
-
---
|
4
|
-
|
5
|
-
# CORS Plugin
|
6
|
-
|
7
|
-
The CORS (Cross-Origin Resource Sharing) plugin enables cross-origin requests in your Balda.js application. It handles preflight requests and sets appropriate CORS headers for both simple and complex requests.
|
8
|
-
|
9
|
-
## Features
|
10
|
-
|
11
|
-
- **Automatic Preflight Handling**: Automatically handles OPTIONS requests
|
12
|
-
- **Flexible Origin Configuration**: Support for strings, arrays, and regex patterns
|
13
|
-
- **Customizable Headers**: Configurable allowed and exposed headers
|
14
|
-
- **Credentials Support**: Enable/disable credentials for cross-origin requests
|
15
|
-
- **Method Control**: Specify allowed HTTP methods
|
16
|
-
|
17
|
-
## Basic Configuration
|
18
|
-
|
19
|
-
### Simple Setup
|
20
|
-
|
21
|
-
```typescript
|
22
|
-
import { Server } from 'balda-js';
|
23
|
-
|
24
|
-
const server = new Server({
|
25
|
-
port: 3000,
|
26
|
-
plugins: {
|
27
|
-
cors: {
|
28
|
-
origin: '*'
|
29
|
-
}
|
30
|
-
}
|
31
|
-
});
|
32
|
-
```
|
33
|
-
|
34
|
-
### Development Configuration
|
35
|
-
|
36
|
-
```typescript
|
37
|
-
const server = new Server({
|
38
|
-
port: 3000,
|
39
|
-
plugins: {
|
40
|
-
cors: {
|
41
|
-
origin: ['http://localhost:3000', 'http://localhost:3001'],
|
42
|
-
credentials: true,
|
43
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
44
|
-
allowedHeaders: ['Content-Type', 'Authorization']
|
45
|
-
}
|
46
|
-
}
|
47
|
-
});
|
48
|
-
```
|
49
|
-
|
50
|
-
## Configuration Options
|
51
|
-
|
52
|
-
### Origin Configuration
|
53
|
-
|
54
|
-
```typescript
|
55
|
-
// Allow all origins (not recommended for production)
|
56
|
-
cors: {
|
57
|
-
origin: '*'
|
58
|
-
}
|
59
|
-
|
60
|
-
// Allow specific origins
|
61
|
-
cors: {
|
62
|
-
origin: ['https://myapp.com', 'https://admin.myapp.com']
|
63
|
-
}
|
64
|
-
|
65
|
-
// Allow origins with regex patterns
|
66
|
-
cors: {
|
67
|
-
origin: [
|
68
|
-
'https://myapp.com',
|
69
|
-
/^https:\/\/.*\.myapp\.com$/
|
70
|
-
]
|
71
|
-
}
|
72
|
-
|
73
|
-
// Function-based origin validation
|
74
|
-
cors: {
|
75
|
-
origin: (origin) => {
|
76
|
-
const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com'];
|
77
|
-
return allowedOrigins.includes(origin);
|
78
|
-
}
|
79
|
-
}
|
80
|
-
```
|
81
|
-
|
82
|
-
### Methods Configuration
|
83
|
-
|
84
|
-
```typescript
|
85
|
-
// Default methods (GET, HEAD, PUT, PATCH, POST, DELETE)
|
86
|
-
cors: {
|
87
|
-
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE']
|
88
|
-
}
|
89
|
-
|
90
|
-
// Custom methods
|
91
|
-
cors: {
|
92
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE']
|
93
|
-
}
|
94
|
-
|
95
|
-
// String format
|
96
|
-
cors: {
|
97
|
-
methods: 'GET,POST,PUT,DELETE'
|
98
|
-
}
|
99
|
-
```
|
100
|
-
|
101
|
-
### Headers Configuration
|
102
|
-
|
103
|
-
```typescript
|
104
|
-
// Allow specific headers
|
105
|
-
cors: {
|
106
|
-
allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key']
|
107
|
-
}
|
108
|
-
|
109
|
-
// Allow all headers
|
110
|
-
cors: {
|
111
|
-
allowedHeaders: '*'
|
112
|
-
}
|
113
|
-
|
114
|
-
// Expose headers to the client
|
115
|
-
cors: {
|
116
|
-
exposedHeaders: ['X-Total-Count', 'X-Page-Count']
|
117
|
-
}
|
118
|
-
```
|
119
|
-
|
120
|
-
### Advanced Configuration
|
121
|
-
|
122
|
-
```typescript
|
123
|
-
const server = new Server({
|
124
|
-
port: 3000,
|
125
|
-
plugins: {
|
126
|
-
cors: {
|
127
|
-
origin: ['https://myapp.com', 'https://admin.myapp.com'],
|
128
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
129
|
-
allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'],
|
130
|
-
exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
|
131
|
-
credentials: true,
|
132
|
-
maxAge: 86400, // 24 hours
|
133
|
-
preflightContinue: false,
|
134
|
-
optionsSuccessStatus: 204
|
135
|
-
}
|
136
|
-
}
|
137
|
-
});
|
138
|
-
```
|
139
|
-
|
140
|
-
## Environment-Based Configuration
|
141
|
-
|
142
|
-
```typescript
|
143
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
144
|
-
|
145
|
-
const server = new Server({
|
146
|
-
port: 3000,
|
147
|
-
plugins: {
|
148
|
-
cors: {
|
149
|
-
origin: isProduction
|
150
|
-
? ['https://myapp.com']
|
151
|
-
: ['http://localhost:3000', 'http://localhost:3001'],
|
152
|
-
credentials: isProduction,
|
153
|
-
maxAge: isProduction ? 86400 : undefined
|
154
|
-
}
|
155
|
-
}
|
156
|
-
});
|
157
|
-
```
|
158
|
-
|
159
|
-
## Route-Level CORS
|
160
|
-
|
161
|
-
You can also apply CORS middleware to specific routes:
|
162
|
-
|
163
|
-
```typescript
|
164
|
-
import { cors } from 'balda-js';
|
165
|
-
|
166
|
-
@controller('/api')
|
167
|
-
export class ApiController {
|
168
|
-
@get('/public', {
|
169
|
-
middleware: [cors({ origin: '*' })]
|
170
|
-
})
|
171
|
-
async publicEndpoint(req: Request, res: Response) {
|
172
|
-
res.json({ message: 'Public endpoint' });
|
173
|
-
}
|
174
|
-
|
175
|
-
@get('/private', {
|
176
|
-
middleware: [cors({
|
177
|
-
origin: ['https://myapp.com'],
|
178
|
-
credentials: true
|
179
|
-
})]
|
180
|
-
})
|
181
|
-
async privateEndpoint(req: Request, res: Response) {
|
182
|
-
res.json({ message: 'Private endpoint' });
|
183
|
-
}
|
184
|
-
}
|
185
|
-
```
|
186
|
-
|
187
|
-
## CORS Headers Explained
|
188
|
-
|
189
|
-
### Request Headers
|
190
|
-
|
191
|
-
- **Origin**: The origin of the request (set by browser)
|
192
|
-
- **Access-Control-Request-Method**: The HTTP method for the actual request
|
193
|
-
- **Access-Control-Request-Headers**: The headers for the actual request
|
194
|
-
|
195
|
-
### Response Headers
|
196
|
-
|
197
|
-
- **Access-Control-Allow-Origin**: Which origins are allowed
|
198
|
-
- **Access-Control-Allow-Methods**: Which HTTP methods are allowed
|
199
|
-
- **Access-Control-Allow-Headers**: Which headers are allowed
|
200
|
-
- **Access-Control-Expose-Headers**: Which headers are exposed to the client
|
201
|
-
- **Access-Control-Allow-Credentials**: Whether credentials are allowed
|
202
|
-
- **Access-Control-Max-Age**: How long preflight results can be cached
|
203
|
-
|
204
|
-
## Preflight Requests
|
205
|
-
|
206
|
-
CORS automatically handles preflight OPTIONS requests:
|
207
|
-
|
208
|
-
```typescript
|
209
|
-
// This OPTIONS request is handled automatically
|
210
|
-
// OPTIONS /api/users
|
211
|
-
// Origin: https://myapp.com
|
212
|
-
// Access-Control-Request-Method: POST
|
213
|
-
// Access-Control-Request-Headers: Content-Type, Authorization
|
214
|
-
|
215
|
-
// Response:
|
216
|
-
// Access-Control-Allow-Origin: https://myapp.com
|
217
|
-
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
|
218
|
-
// Access-Control-Allow-Headers: Content-Type, Authorization
|
219
|
-
// Access-Control-Max-Age: 86400
|
220
|
-
```
|
221
|
-
|
222
|
-
## Security Considerations
|
223
|
-
|
224
|
-
### Production Configuration
|
225
|
-
|
226
|
-
```typescript
|
227
|
-
// Secure production configuration
|
228
|
-
cors: {
|
229
|
-
origin: ['https://myapp.com'], // Specific origins only
|
230
|
-
credentials: true,
|
231
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
232
|
-
allowedHeaders: ['Content-Type', 'Authorization'],
|
233
|
-
maxAge: 86400
|
234
|
-
}
|
235
|
-
```
|
236
|
-
|
237
|
-
### Development vs Production
|
238
|
-
|
239
|
-
```typescript
|
240
|
-
const corsConfig = process.env.NODE_ENV === 'production'
|
241
|
-
? {
|
242
|
-
origin: ['https://myapp.com'],
|
243
|
-
credentials: true,
|
244
|
-
maxAge: 86400
|
245
|
-
}
|
246
|
-
: {
|
247
|
-
origin: ['http://localhost:3000'],
|
248
|
-
credentials: false
|
249
|
-
};
|
250
|
-
|
251
|
-
const server = new Server({
|
252
|
-
port: 3000,
|
253
|
-
plugins: {
|
254
|
-
cors: corsConfig
|
255
|
-
}
|
256
|
-
});
|
257
|
-
```
|
258
|
-
|
259
|
-
## Common Issues and Solutions
|
260
|
-
|
261
|
-
### Credentials with Wildcard Origin
|
262
|
-
|
263
|
-
```typescript
|
264
|
-
// ❌ This won't work - credentials require specific origins
|
265
|
-
cors: {
|
266
|
-
origin: '*',
|
267
|
-
credentials: true
|
268
|
-
}
|
269
|
-
|
270
|
-
// ✅ This works - specific origins with credentials
|
271
|
-
cors: {
|
272
|
-
origin: ['https://myapp.com'],
|
273
|
-
credentials: true
|
274
|
-
}
|
275
|
-
```
|
276
|
-
|
277
|
-
### Multiple Origins with Credentials
|
278
|
-
|
279
|
-
```typescript
|
280
|
-
// ✅ Multiple specific origins with credentials
|
281
|
-
cors: {
|
282
|
-
origin: ['https://myapp.com', 'https://admin.myapp.com'],
|
283
|
-
credentials: true
|
284
|
-
}
|
285
|
-
```
|
286
|
-
|
287
|
-
### Custom Headers
|
288
|
-
|
289
|
-
```typescript
|
290
|
-
// ✅ Include custom headers
|
291
|
-
cors: {
|
292
|
-
allowedHeaders: ['Content-Type', 'Authorization', 'X-Custom-Header'],
|
293
|
-
exposedHeaders: ['X-Total-Count', 'X-Page-Count']
|
294
|
-
}
|
295
|
-
```
|