balda-js 0.0.1

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.
Files changed (69) hide show
  1. package/.github/workflows/publish.yml +38 -0
  2. package/.husky/pre-commit +19 -0
  3. package/.nvmrc +1 -0
  4. package/LICENSE +21 -0
  5. package/README.md +46 -0
  6. package/deno.lock +2454 -0
  7. package/docs/README.md +135 -0
  8. package/docs/blog/authors.yml +6 -0
  9. package/docs/blog/tags.yml +4 -0
  10. package/docs/cli.md +109 -0
  11. package/docs/docs/core-concepts/controllers.md +393 -0
  12. package/docs/docs/core-concepts/middleware.md +302 -0
  13. package/docs/docs/core-concepts/request-response.md +486 -0
  14. package/docs/docs/core-concepts/routing.md +388 -0
  15. package/docs/docs/core-concepts/server.md +332 -0
  16. package/docs/docs/cron/overview.md +70 -0
  17. package/docs/docs/examples/rest-api.md +595 -0
  18. package/docs/docs/getting-started/configuration.md +168 -0
  19. package/docs/docs/getting-started/installation.md +125 -0
  20. package/docs/docs/getting-started/quick-start.md +273 -0
  21. package/docs/docs/intro.md +46 -0
  22. package/docs/docs/plugins/cookie.md +424 -0
  23. package/docs/docs/plugins/cors.md +295 -0
  24. package/docs/docs/plugins/file.md +382 -0
  25. package/docs/docs/plugins/helmet.md +388 -0
  26. package/docs/docs/plugins/json.md +338 -0
  27. package/docs/docs/plugins/log.md +592 -0
  28. package/docs/docs/plugins/overview.md +390 -0
  29. package/docs/docs/plugins/rate-limiter.md +347 -0
  30. package/docs/docs/plugins/static.md +352 -0
  31. package/docs/docs/plugins/swagger.md +411 -0
  32. package/docs/docs/plugins/urlencoded.md +76 -0
  33. package/docs/docs/testing/examples.md +384 -0
  34. package/docs/docs/testing/mock-server.md +311 -0
  35. package/docs/docs/testing/overview.md +76 -0
  36. package/docs/docusaurus.config.ts +144 -0
  37. package/docs/intro.md +78 -0
  38. package/docs/package.json +46 -0
  39. package/docs/sidebars.ts +72 -0
  40. package/docs/static/.nojekyll +0 -0
  41. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  42. package/docs/static/img/docusaurus.png +0 -0
  43. package/docs/static/img/favicon.ico +0 -0
  44. package/docs/static/img/logo.svg +1 -0
  45. package/docs/static/img/undraw_docusaurus_mountain.svg +37 -0
  46. package/docs/static/img/undraw_docusaurus_react.svg +170 -0
  47. package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  48. package/docs/tsconfig.json +8 -0
  49. package/package.json +91 -0
  50. package/speed_test.sh +3 -0
  51. package/test/benchmark/index.ts +17 -0
  52. package/test/cli/cli.ts +7 -0
  53. package/test/commands/test.ts +42 -0
  54. package/test/controllers/file_upload.ts +29 -0
  55. package/test/controllers/urlencoded.ts +13 -0
  56. package/test/controllers/users.ts +111 -0
  57. package/test/cron/index.ts +6 -0
  58. package/test/cron/test_cron.ts +8 -0
  59. package/test/cron/test_cron_imported.ts +8 -0
  60. package/test/native_env.ts +16 -0
  61. package/test/resources/test.txt +1 -0
  62. package/test/server/index.ts +3 -0
  63. package/test/server/instance.ts +63 -0
  64. package/test/suite/upload.test.ts +23 -0
  65. package/test/suite/urlencoded.test.ts +23 -0
  66. package/test/suite/users.test.ts +76 -0
  67. package/todo.md +9 -0
  68. package/tsconfig.json +24 -0
  69. package/vitest.config.ts +17 -0
@@ -0,0 +1,486 @@
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.