balda-js 0.0.1 → 0.0.3
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/package.json +1 -6
- 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,486 +0,0 @@
|
|
1
|
-
---
|
2
|
-
sidebar_position: 5
|
3
|
-
---
|
4
|
-
|
5
|
-
# Request & Response
|
6
|
-
|
7
|
-
Balda.js provides a clean and intuitive API for handling HTTP requests and responses. The framework extends the standard Node.js request and response objects with additional convenience methods and properties.
|
8
|
-
|
9
|
-
## Request Object
|
10
|
-
|
11
|
-
The request object contains information about the incoming HTTP request.
|
12
|
-
|
13
|
-
### Basic Properties
|
14
|
-
|
15
|
-
```typescript
|
16
|
-
interface Request {
|
17
|
-
method: string; // HTTP method (GET, POST, etc.)
|
18
|
-
url: string; // Request URL
|
19
|
-
path: string; // Request path
|
20
|
-
headers: Record<string, string>; // Request headers
|
21
|
-
query: Record<string, string>; // Query parameters
|
22
|
-
params: Record<string, string>; // Route parameters
|
23
|
-
body: any; // Request body (parsed)
|
24
|
-
ip: string; // Client IP address
|
25
|
-
cookies: Record<string, string>; // Request cookies
|
26
|
-
}
|
27
|
-
```
|
28
|
-
|
29
|
-
### Accessing Request Data
|
30
|
-
|
31
|
-
```typescript
|
32
|
-
@get('/users/:id')
|
33
|
-
async getUser(req: Request, res: Response) {
|
34
|
-
// Route parameters
|
35
|
-
const userId = req.params.id;
|
36
|
-
|
37
|
-
// Query parameters
|
38
|
-
const { page = 1, limit = 10 } = req.query;
|
39
|
-
|
40
|
-
// Headers
|
41
|
-
const userAgent = req.headers['user-agent'];
|
42
|
-
const authToken = req.headers.authorization;
|
43
|
-
|
44
|
-
// Request body (for POST/PUT/PATCH)
|
45
|
-
const userData = req.body;
|
46
|
-
|
47
|
-
// Client IP
|
48
|
-
const clientIP = req.ip;
|
49
|
-
|
50
|
-
res.json({ userId, page, limit, userAgent, clientIP });
|
51
|
-
}
|
52
|
-
```
|
53
|
-
|
54
|
-
### File Uploads
|
55
|
-
|
56
|
-
```typescript
|
57
|
-
import { Request, Response, fileParser } from 'balda-js';
|
58
|
-
|
59
|
-
@post('/upload')
|
60
|
-
@middleware(fileParser())
|
61
|
-
async uploadFile(req: Request, res: Response) {
|
62
|
-
const file = req.file('file');
|
63
|
-
|
64
|
-
if (!file) {
|
65
|
-
return res.badRequest({ error: 'No file uploaded' });
|
66
|
-
}
|
67
|
-
|
68
|
-
res.json({
|
69
|
-
filename: file.originalName,
|
70
|
-
size: file.size,
|
71
|
-
mimetype: file.mimeType
|
72
|
-
});
|
73
|
-
}
|
74
|
-
```
|
75
|
-
|
76
|
-
## Response Object
|
77
|
-
|
78
|
-
The response object provides methods for sending HTTP responses.
|
79
|
-
|
80
|
-
### Status Methods
|
81
|
-
|
82
|
-
```typescript
|
83
|
-
import { Request, Response } from 'balda-js';
|
84
|
-
|
85
|
-
@get('/users')
|
86
|
-
async getUsers(req: Request, res: Response) {
|
87
|
-
// Success responses
|
88
|
-
res.ok({ users: [] }); // 200 OK
|
89
|
-
res.created({ id: 1 }); // 201 Created
|
90
|
-
res.noContent(); // 204 No Content
|
91
|
-
|
92
|
-
// Client error responses
|
93
|
-
res.badRequest({ error: 'Invalid data' }); // 400 Bad Request
|
94
|
-
res.unauthorized({ error: 'Auth required' }); // 401 Unauthorized
|
95
|
-
res.forbidden({ error: 'Access denied' }); // 403 Forbidden
|
96
|
-
res.notFound({ error: 'User not found' }); // 404 Not Found
|
97
|
-
res.conflict({ error: 'User exists' }); // 409 Conflict
|
98
|
-
res.tooManyRequests({ error: 'Rate limited' }); // 429 Too Many Requests
|
99
|
-
|
100
|
-
// Server error responses
|
101
|
-
res.internalServerError({ error: 'Server error' }); // 500 Internal Server Error
|
102
|
-
res.notImplemented({ error: 'Not implemented' }); // 501 Not Implemented
|
103
|
-
res.badGateway({ error: 'Bad gateway' }); // 502 Bad Gateway
|
104
|
-
res.serviceUnavailable({ error: 'Unavailable' }); // 503 Service Unavailable
|
105
|
-
}
|
106
|
-
```
|
107
|
-
|
108
|
-
### Content Methods
|
109
|
-
|
110
|
-
```typescript
|
111
|
-
import { Request, Response } from 'balda-js';
|
112
|
-
|
113
|
-
@get('/api/data')
|
114
|
-
async getData(req: Request, res: Response) {
|
115
|
-
// Manually set headers
|
116
|
-
res.setHeader('X-Custom-Header', 'value');
|
117
|
-
|
118
|
-
// JSON response
|
119
|
-
res.json({ message: 'Hello World' });
|
120
|
-
|
121
|
-
// Text response
|
122
|
-
res.text('Hello World');
|
123
|
-
|
124
|
-
// HTML response
|
125
|
-
res.html('<h1>Hello World</h1>');
|
126
|
-
|
127
|
-
// Redirect
|
128
|
-
res.redirect('/new-location');
|
129
|
-
|
130
|
-
// Download file
|
131
|
-
res.download('/path/to/file.pdf', 'filename.pdf');
|
132
|
-
|
133
|
-
// Send tries to understand the best content type to send based on the body (better to use specific methods)
|
134
|
-
res.send({ message: 'Hello World' });
|
135
|
-
}
|
136
|
-
```
|
137
|
-
|
138
|
-
### Header Management
|
139
|
-
|
140
|
-
```typescript
|
141
|
-
import { Request, Response } from 'balda-js';
|
142
|
-
|
143
|
-
@get('/api/data')
|
144
|
-
async getData(req: Request, res: Response) {
|
145
|
-
// Set headers
|
146
|
-
res.setHeader('Content-Type', 'application/json');
|
147
|
-
res.setHeader('Cache-Control', 'no-cache');
|
148
|
-
|
149
|
-
// Set multiple headers
|
150
|
-
res.setHeaders({
|
151
|
-
'Content-Type': 'application/json',
|
152
|
-
'X-Custom-Header': 'value'
|
153
|
-
});
|
154
|
-
|
155
|
-
// Get headers
|
156
|
-
const contentType = res.getHeader('Content-Type');
|
157
|
-
|
158
|
-
res.json({ data: 'value' });
|
159
|
-
}
|
160
|
-
```
|
161
|
-
|
162
|
-
### Cookie Management
|
163
|
-
|
164
|
-
```typescript
|
165
|
-
import { Request, Response } from 'balda-js';
|
166
|
-
|
167
|
-
@post('/login')
|
168
|
-
async login(req: Request, res: Response) {
|
169
|
-
// Set cookies
|
170
|
-
res.setCookie('sessionId', 'abc123', {
|
171
|
-
httpOnly: true,
|
172
|
-
secure: true,
|
173
|
-
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
174
|
-
});
|
175
|
-
|
176
|
-
// Clear cookies
|
177
|
-
res.clearCookie('oldSession');
|
178
|
-
|
179
|
-
res.json({ message: 'Logged in' });
|
180
|
-
}
|
181
|
-
```
|
182
|
-
|
183
|
-
## Request Validation
|
184
|
-
|
185
|
-
### Built-in Validation
|
186
|
-
|
187
|
-
The built in validation is based on the `@sinclair/typebox` library, you can use it to validate the request body or query parameters.
|
188
|
-
The validated body is passed as the next argument to the route handler (can be stacked with other validation decorators).
|
189
|
-
|
190
|
-
```typescript
|
191
|
-
import { Request, Response, validate } from 'balda-js';
|
192
|
-
import { Type, Static } from '@sinclair/typebox';
|
193
|
-
|
194
|
-
const CreateUserSchema = Type.Object({
|
195
|
-
name: Type.String({ minLength: 1 }),
|
196
|
-
email: Type.String({ format: 'email' }),
|
197
|
-
age: Type.Number({ minimum: 18 })
|
198
|
-
});
|
199
|
-
|
200
|
-
@post('/users')
|
201
|
-
@validate.body(CreateUserSchema)
|
202
|
-
async createUser(req: Request, res: Response, body: Static<typeof CreateUserSchema>) {
|
203
|
-
// body is validated and typed
|
204
|
-
const { name, email, age } = body;
|
205
|
-
|
206
|
-
res.created({ name, email, age });
|
207
|
-
}
|
208
|
-
```
|
209
|
-
|
210
|
-
### Query Parameter Validation
|
211
|
-
|
212
|
-
```typescript
|
213
|
-
import { Request, Response, validate } from 'balda-js';
|
214
|
-
import { Type, Static } from '@sinclair/typebox';
|
215
|
-
|
216
|
-
const QuerySchema = Type.Object({
|
217
|
-
page: Type.Optional(Type.Number({ minimum: 1 })),
|
218
|
-
limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100 }))
|
219
|
-
});
|
220
|
-
|
221
|
-
@get('/users')
|
222
|
-
@validate.query(QuerySchema)
|
223
|
-
async getUsers(req: Request, res: Response, query: any) {
|
224
|
-
const { page = 1, limit = 10 } = query;
|
225
|
-
res.json({ page, limit });
|
226
|
-
}
|
227
|
-
```
|
228
|
-
|
229
|
-
## Response Serialization
|
230
|
-
|
231
|
-
### Schema-based Serialization
|
232
|
-
|
233
|
-
```typescript
|
234
|
-
import { Request, Response, serialize } from 'balda-js';
|
235
|
-
import { Type } from '@sinclair/typebox';
|
236
|
-
|
237
|
-
const UserSchema = Type.Object({
|
238
|
-
id: Type.Number(),
|
239
|
-
name: Type.String(),
|
240
|
-
email: Type.String()
|
241
|
-
});
|
242
|
-
|
243
|
-
@get('/users')
|
244
|
-
@serialize(Type.Array(UserSchema))
|
245
|
-
async getUsers(req: Request, res: Response) {
|
246
|
-
const users = await userService.findAll();
|
247
|
-
res.json(users);
|
248
|
-
}
|
249
|
-
|
250
|
-
@get('/users/:id')
|
251
|
-
@serialize(UserSchema)
|
252
|
-
async getUser(req: Request, res: Response) {
|
253
|
-
const user = await userService.findById(req.params.id);
|
254
|
-
res.json(user);
|
255
|
-
}
|
256
|
-
```
|
257
|
-
|
258
|
-
### Multiple Response Schemas
|
259
|
-
|
260
|
-
```typescript
|
261
|
-
import { Request, Response, serialize } from 'balda-js';
|
262
|
-
import { Type } from '@sinclair/typebox';
|
263
|
-
|
264
|
-
@get('/users/:id')
|
265
|
-
@serialize(UserSchema)
|
266
|
-
@serialize(Type.Object({ error: Type.String() }), { status: 404 })
|
267
|
-
async getUser(req: Request, res: Response) {
|
268
|
-
const user = await userService.findById(req.params.id);
|
269
|
-
|
270
|
-
if (!user) {
|
271
|
-
return res.notFound({ error: 'User not found' });
|
272
|
-
}
|
273
|
-
|
274
|
-
res.json(user);
|
275
|
-
}
|
276
|
-
```
|
277
|
-
|
278
|
-
## Error Handling
|
279
|
-
|
280
|
-
### Custom Error Responses
|
281
|
-
|
282
|
-
```typescript
|
283
|
-
import { Request, Response } from 'balda-js';
|
284
|
-
|
285
|
-
@get('/users/:id')
|
286
|
-
async getUser(req: Request, res: Response) {
|
287
|
-
try {
|
288
|
-
const user = await userService.findById(req.params.id);
|
289
|
-
|
290
|
-
if (!user) {
|
291
|
-
return res.notFound({
|
292
|
-
error: 'User not found',
|
293
|
-
code: 'USER_NOT_FOUND'
|
294
|
-
});
|
295
|
-
}
|
296
|
-
|
297
|
-
res.json(user);
|
298
|
-
} catch (error) {
|
299
|
-
console.error('Error fetching user:', error);
|
300
|
-
res.internalServerError({
|
301
|
-
error: 'Failed to fetch user',
|
302
|
-
code: 'FETCH_ERROR'
|
303
|
-
});
|
304
|
-
}
|
305
|
-
}
|
306
|
-
```
|
307
|
-
|
308
|
-
### Global Error Handler
|
309
|
-
|
310
|
-
```typescript
|
311
|
-
import { Server } from 'balda-js';
|
312
|
-
|
313
|
-
server.setErrorHandler((req, res, next, error) => {
|
314
|
-
console.error('Error:', error);
|
315
|
-
|
316
|
-
if (error.name === 'ValidationError') {
|
317
|
-
return res.badRequest({
|
318
|
-
error: 'Validation failed',
|
319
|
-
details: error.message
|
320
|
-
});
|
321
|
-
}
|
322
|
-
|
323
|
-
if (error.name === 'UnauthorizedError') {
|
324
|
-
return res.unauthorized({ error: 'Authentication required' });
|
325
|
-
}
|
326
|
-
|
327
|
-
res.internalServerError({ error: 'Internal server error' });
|
328
|
-
});
|
329
|
-
```
|
330
|
-
|
331
|
-
## Request Lifecycle
|
332
|
-
|
333
|
-
### Middleware Chain
|
334
|
-
|
335
|
-
```typescript
|
336
|
-
import { Server, controller, get, post, middleware, validate } from 'balda-js';
|
337
|
-
import { Request, Response } from 'balda-js';
|
338
|
-
import { Type } from '@sinclair/typebox';
|
339
|
-
|
340
|
-
// 1. Global middleware
|
341
|
-
server.use(logger);
|
342
|
-
server.use(cors);
|
343
|
-
|
344
|
-
// 2. Controller middleware
|
345
|
-
@controller('/users')
|
346
|
-
@middleware(authMiddleware)
|
347
|
-
export class UsersController {
|
348
|
-
|
349
|
-
// 3. Route middleware
|
350
|
-
@get('/:id', { middleware: [rateLimit] })
|
351
|
-
// 4. Validation
|
352
|
-
@validate.params(Type.Object({ id: Type.String() }))
|
353
|
-
// 5. Route handler
|
354
|
-
async getUser(req: Request, res: Response) {
|
355
|
-
// 6. Response serialization
|
356
|
-
res.json({ user: req.params.id });
|
357
|
-
}
|
358
|
-
}
|
359
|
-
```
|
360
|
-
|
361
|
-
## TypeScript Support
|
362
|
-
|
363
|
-
### Request Extensions
|
364
|
-
|
365
|
-
```typescript
|
366
|
-
import { Request, Response } from 'balda-js';
|
367
|
-
|
368
|
-
interface AuthenticatedRequest extends Request {
|
369
|
-
user: {
|
370
|
-
id: number;
|
371
|
-
email: string;
|
372
|
-
role: string;
|
373
|
-
};
|
374
|
-
}
|
375
|
-
|
376
|
-
@get('/profile')
|
377
|
-
@middleware(authMiddleware)
|
378
|
-
async getProfile(req: AuthenticatedRequest, res: Response) {
|
379
|
-
// req.user is now typed
|
380
|
-
res.json({
|
381
|
-
id: req.user.id,
|
382
|
-
email: req.user.email,
|
383
|
-
role: req.user.role
|
384
|
-
});
|
385
|
-
}
|
386
|
-
```
|
387
|
-
|
388
|
-
### Response Types
|
389
|
-
|
390
|
-
```typescript
|
391
|
-
import { Request, Response } from 'balda-js';
|
392
|
-
|
393
|
-
interface ApiResponse<T> {
|
394
|
-
data: T;
|
395
|
-
message?: string;
|
396
|
-
timestamp: string;
|
397
|
-
}
|
398
|
-
|
399
|
-
@get('/users')
|
400
|
-
async getUsers(req: Request, res: Response) {
|
401
|
-
const users = await userService.findAll();
|
402
|
-
|
403
|
-
const response: ApiResponse<typeof users> = {
|
404
|
-
data: users,
|
405
|
-
message: 'Users retrieved successfully',
|
406
|
-
timestamp: new Date().toISOString()
|
407
|
-
};
|
408
|
-
|
409
|
-
res.json(response);
|
410
|
-
}
|
411
|
-
```
|
412
|
-
|
413
|
-
## Best Practices
|
414
|
-
|
415
|
-
### 1. Consistent Response Format
|
416
|
-
|
417
|
-
```typescript
|
418
|
-
// Good: Consistent structure
|
419
|
-
res.json({
|
420
|
-
success: true,
|
421
|
-
data: { id: 1, name: 'John' },
|
422
|
-
message: 'User created successfully'
|
423
|
-
});
|
424
|
-
|
425
|
-
// Error response
|
426
|
-
res.badRequest({
|
427
|
-
success: false,
|
428
|
-
error: 'Validation failed',
|
429
|
-
details: ['Name is required']
|
430
|
-
});
|
431
|
-
```
|
432
|
-
|
433
|
-
### 2. Proper Status Codes
|
434
|
-
|
435
|
-
```typescript
|
436
|
-
// Use appropriate status codes
|
437
|
-
res.created({ id: 1 }); // 201 for new resources
|
438
|
-
res.noContent(); // 204 for successful deletion
|
439
|
-
res.badRequest({ error: '' }); // 400 for client errors
|
440
|
-
res.notFound({ error: '' }); // 404 for missing resources
|
441
|
-
```
|
442
|
-
|
443
|
-
### 3. Input Validation
|
444
|
-
|
445
|
-
```typescript
|
446
|
-
@post('/users')
|
447
|
-
@validate.body(CreateUserSchema)
|
448
|
-
async createUser(req: Request, res: Response, body: CreateUser) {
|
449
|
-
// body is validated and typed
|
450
|
-
const user = await userService.create(body);
|
451
|
-
res.created(user);
|
452
|
-
}
|
453
|
-
```
|
454
|
-
|
455
|
-
### 4. Error Handling
|
456
|
-
|
457
|
-
```typescript
|
458
|
-
@get('/users/:id')
|
459
|
-
async getUser(req: Request, res: Response) {
|
460
|
-
try {
|
461
|
-
const user = await userService.findById(req.params.id);
|
462
|
-
|
463
|
-
if (!user) {
|
464
|
-
return res.notFound({ error: 'User not found' });
|
465
|
-
}
|
466
|
-
|
467
|
-
res.json(user);
|
468
|
-
} catch (error) {
|
469
|
-
console.error('Database error:', error);
|
470
|
-
res.internalServerError({ error: 'Failed to fetch user' });
|
471
|
-
}
|
472
|
-
}
|
473
|
-
```
|
474
|
-
|
475
|
-
### 5. Security Headers
|
476
|
-
|
477
|
-
```typescript
|
478
|
-
// Set security headers
|
479
|
-
res.setHeaders({
|
480
|
-
'X-Content-Type-Options': 'nosniff',
|
481
|
-
'X-Frame-Options': 'DENY',
|
482
|
-
'X-XSS-Protection': '1; mode=block'
|
483
|
-
});
|
484
|
-
```
|
485
|
-
|
486
|
-
The request and response objects in Balda.js provide a powerful and intuitive API for building robust web applications with proper error handling, validation, and type safety.
|