@je-es/server 0.1.1 → 0.1.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/README.md CHANGED
@@ -8,10 +8,13 @@
8
8
  </div>
9
9
 
10
10
  <div align="center">
11
- <img src="https://img.shields.io/badge/v-1.1.0-black"/>
12
- <a href="https://github.com/maysara-elshewehy">
13
- </a>
14
- <a href="https://github.com/je-es"> <img src="https://img.shields.io/badge/@-je--es-black"/> </a>
11
+ <img src="https://img.shields.io/badge/v-0.1.3-black"/>
12
+ <img src="https://img.shields.io/badge/🔥-@je--es-black"/>
13
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
14
+ <img src="https://github.com/je-es/server/actions/workflows/ci.yml/badge.svg" alt="CI" />
15
+ <img src="https://img.shields.io/badge/coverage-100%25-brightgreen" alt="Test Coverage" />
16
+ <img src="https://img.shields.io/github/issues/je-es/server?style=flat" alt="Github Repo Issues" />
17
+ <img src="https://img.shields.io/github/stars/je-es/server?style=social" alt="GitHub Repo stars" />
15
18
  </div>
16
19
  <br>
17
20
 
@@ -22,173 +25,177 @@
22
25
 
23
26
  <!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
24
27
 
25
-
26
28
  - ## Quick Start 🔥
27
29
 
28
- > _**The simplest, fastest, most organized and stable way to build servers.**_
30
+ > _**The simplest, fastest, and most organized way to build production-ready servers with Bun.**_
29
31
 
30
32
  > _We prefer to use [`space`](https://github.com//solution-lib/space) with [`@solution-dist/server`](https://github.com/solution-dist/server) for a better experience._
31
33
 
32
- ```bash
33
- space init <server-name> -t server
34
- ```
34
+ - ### Setup
35
35
 
36
- ```bash
37
- cd <server-name>
38
- space install
39
- ```
36
+ > install [`space`](https://github.com/solution-lib/space) first.
40
37
 
41
- ```bash
42
- # example
43
- > space start
38
+ - #### Create
44
39
 
45
- # output
46
- 16:16:31 Server started at http://localhost:3000
47
- 16:17:25 GET / 200 4ms
48
- ...
49
- ```
40
+ ```bash
41
+ > space init <name> -t server # This will clone a ready-to-use repo and make some changes to suit your server.
42
+ > cd <name> # Go to the project directory
43
+ > space install # Install the dependencies
44
+ ```
50
45
 
51
- <br>
46
+ - #### Manage
47
+
48
+ ```bash
49
+ > space lint
50
+ > space build
51
+ > space test
52
+ > space start
53
+ ```
54
+
55
+ - #### Usage
52
56
 
57
+ ```typescript
58
+ import { server } from '@je-es/server';
59
+
60
+ const app = server({
61
+ port : 3000,
62
+ routes : [
63
+ {
64
+ method : 'GET',
65
+ path : '/',
66
+ handler : (c) => c.json({ message: 'Hello World!' })
67
+ }
68
+ ]
69
+ });
70
+
71
+ await app.start();
72
+ ```
73
+
74
+ ```bash
75
+ > space start
76
+ 16:16:31 Server started at http://localhost:3000
77
+ 16:17:25 GET / 200 1ms
78
+ ...
79
+ ```
80
+
81
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
53
82
 
54
83
  - ## Examples
55
84
 
56
85
  - ### Basic Server
57
86
 
58
87
  ```typescript
59
- // import
60
88
  import { server } from '@je-es/server';
61
- ```
62
89
 
63
- ```typescript
64
- // create
65
90
  const app = server({
66
91
  port : 3000,
92
+ logging : { level: 'info', pretty: true },
67
93
  routes : [
68
- {
69
- method : 'GET',
70
- path : '/',
71
- handler : (c) => c.json({ message: 'Hello World!' })
72
- },
73
94
  {
74
95
  method : 'GET',
75
96
  path : '/users/:id',
76
- handler : (c) => {
77
- const userId = c.params.id;
78
- return c.json({ userId, name: 'John Doe' });
79
- }
97
+ handler : (c) => c.json({
98
+ userId : c.params.id,
99
+ ip : c.ip,
100
+ requestId : c.requestId
101
+ })
80
102
  },
81
103
  {
82
- method : 'POST',
83
- path : '/users',
84
- handler : (c) => {
85
- const userData = c.body;
86
- return c.json({ created: true, data: userData });
87
- }
104
+ method : 'POST',
105
+ path : '/users',
106
+ handler : (c) => c.status(201).json({
107
+ created : true,
108
+ data : c.body
109
+ })
88
110
  }
89
111
  ]
90
112
  });
91
- ```
92
113
 
93
- ```typescript
94
- // start
95
114
  await app.start();
96
115
  ```
97
116
 
98
- > use `space start` to run your server.
99
-
100
- ```typescript
101
- > space start
102
-
103
- → URL: http://localhost:3000
104
- → Environment: test
105
- → Routes: N
106
- ...
107
- ```
108
-
109
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
117
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
110
118
 
111
119
  - ### With Database
112
120
 
113
121
  ```typescript
114
122
  import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
115
123
 
116
- // Define your schema using the built-in schema builder
117
124
  const users = table('users', [
118
- primaryKey(integer('id'), true), // auto-increment primary key
125
+ primaryKey(integer('id'), true),
119
126
  notNull(text('name')),
120
127
  notNull(text('email'))
121
128
  ]);
122
129
 
123
130
  const app = server({
124
- port : 3000,
125
- database: {
126
- connection : './my_app.db', // File-based SQLite database
131
+ port : 3000,
132
+ database : {
133
+ connection : './app.db',
127
134
  schema : { users }
128
135
  },
129
- routes : [
136
+ routes: [
130
137
  {
131
138
  method : 'GET',
132
139
  path : '/users',
133
- handler : (c) => {
134
- const allUsers = c.db.all('users');
135
- return c.json(allUsers);
136
- }
140
+ handler : (c) => c.json(c.db!.all('users'))
137
141
  },
138
142
  {
139
143
  method : 'POST',
140
144
  path : '/users',
145
+ handler : (c) => c.json(c.db!.insert('users', c.body))
146
+ },
147
+ {
148
+ method : 'GET',
149
+ path : '/users/:id',
141
150
  handler : (c) => {
142
- const newUser = c.db.insert('users', c.body);
143
- return c.json(newUser);
151
+ const user = c.db!.findById('users', parseInt(c.params.id));
152
+ return user
153
+ ? c.json(user)
154
+ : c.status(404).json({ error: 'Not found' });
144
155
  }
145
156
  }
146
157
  ]
147
158
  });
148
159
 
149
160
  await app.start();
150
- // Data persists in ./my_app.db file!
151
161
  ```
152
162
 
153
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
163
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
154
164
 
155
165
  - ### With Security
156
166
 
157
167
  ```typescript
158
- import { server } from '@je-es/server';
159
-
160
168
  const app = server({
161
- port : 3000,
162
- security: {
163
- // Rate limiting
164
- rateLimit: {
165
- max : 100,
166
- windowMs : 60000, // 1 minute
167
- message : 'Too many requests, please try again later'
168
- },
169
- // CORS configuration
170
- cors: {
171
- origin : ['http://localhost:3000', 'https://example.com'],
172
- credentials : true,
173
- methods : ['GET', 'POST', 'PUT', 'DELETE'],
174
- allowedHeaders : ['Content-Type', 'Authorization']
169
+ port : 3000,
170
+ security : {
171
+ rateLimit : { max: 100, windowMs: 60000 },
172
+ cors : {
173
+ origin : ['http://localhost:3000'],
174
+ credentials : true
175
175
  }
176
176
  },
177
- routes : [
178
- {
179
- method : 'GET',
180
- path : '/protected',
181
- handler : (c) => c.json({ message: 'This route is protected!' })
182
- }
183
- ]
177
+ routes: [/* your routes */]
184
178
  });
179
+ ```
185
180
 
186
- await app.start();
181
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
182
+
183
+ - ### Static Files
184
+
185
+ ```typescript
186
+ const app = server({
187
+ port : 3000,
188
+ static : {
189
+ path : '/public',
190
+ directory : './public',
191
+ maxAge : 3600
192
+ }
193
+ });
187
194
  ```
188
195
 
189
- <br>
196
+ <br>
190
197
 
191
- - ## API Reference
198
+ - ## API
192
199
 
193
200
  - ### Server Configuration
194
201
 
@@ -196,866 +203,364 @@
196
203
  import { server, type ServerConfig } from '@je-es/server';
197
204
 
198
205
  const config: ServerConfig = {
199
- // Basic settings
200
- port : 3000,
201
- hostname : 'localhost',
206
+ port : 3000,
207
+ hostname : 'localhost',
202
208
 
203
- // Request handling
204
- requestTimeout : 30000, // 30 seconds
205
- maxRequestSize : 10485760, // 10MB
206
- gracefulShutdownTimeout : 10000, // 10 seconds
209
+ // Timeouts & Limits
210
+ requestTimeout : 30000,
211
+ maxRequestSize : 10485760,
212
+ gracefulShutdownTimeout : 10000,
207
213
 
208
- // Logging
214
+ // Logging (via @je-es/slog)
209
215
  logging: {
210
- level : 'info', // 'debug' | 'info' | 'warn' | 'error'
211
- pretty : false // Enable pretty printing
216
+ level : 'info', // 'debug' | 'info' | 'warn' | 'error'
217
+ pretty : false
212
218
  },
213
219
 
214
- // Database
220
+ // Database (via @je-es/sdb)
215
221
  database: {
216
- connection : ':memory:', // or file path like './app.db'
217
- schema : {}
222
+ connection : './app.db', // or ':memory:'
223
+ schema : {}
218
224
  },
219
225
 
226
+ // Multiple databases
227
+ database: [
228
+ { name: 'default', connection: './main.db' },
229
+ { name: 'cache', connection: ':memory:' }
230
+ ],
231
+
220
232
  // Security
221
233
  security: {
222
- rateLimit : true,
223
- cors : true,
224
- csrf : true
234
+ rateLimit : { max: 100, windowMs: 60000 },
235
+ cors : { origin: '*', credentials: true }
225
236
  },
226
237
 
227
- // Routes
228
- routes: [],
229
-
230
- // Lifecycle hooks
231
- onShutdown: async () => {
232
- console.log('Server shutting down...');
233
- }
234
- };
235
-
236
- const app = server(config);
237
- await app.start();
238
- ```
239
-
240
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
241
-
242
- - ### Route Definition
243
-
244
- ```typescript
245
- import { type RouteDefinition, type AppContext } from '@je-es/server';
246
-
247
- // Single HTTP method
248
- const route: RouteDefinition = {
249
- method : 'GET',
250
- path : '/users/:id',
251
- handler : (c: AppContext) => {
252
- return c.json({ id: c.params.id });
253
- }
254
- };
255
-
256
- // Multiple HTTP methods
257
- const multiMethodRoute: RouteDefinition = {
258
- method : ['GET', 'POST'],
259
- path : '/api/resource',
260
- handler : (c: AppContext) => {
261
- if (c.request.method === 'GET') {
262
- return c.json({ method : 'GET' });
263
- }
264
- return c.json({ method : 'POST', body: c.body });
265
- }
266
- };
238
+ // Static files
239
+ static: {
240
+ path : '/public',
241
+ directory : './public',
242
+ maxAge : 3600
243
+ },
267
244
 
268
- // Dynamic routes with nested parameters
269
- const nestedRoute: RouteDefinition = {
270
- method : 'GET',
271
- path : '/posts/:postId/comments/:commentId',
272
- handler : (c: AppContext) => {
273
- return c.json({
274
- postId: c.params.postId,
275
- commentId: c.params.commentId
276
- });
277
- }
245
+ // Lifecycle
246
+ onShutdown : async () => { console.log('Shutting down...'); }
278
247
  };
279
248
  ```
280
249
 
281
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
250
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
282
251
 
283
252
  - ### Context API
284
253
 
285
254
  ```typescript
286
- import { type AppContext } from '@je-es/server';
287
-
288
- // Response methods
289
- handler : (c: AppContext) => {
290
- // JSON response
291
- c.json({ data: 'value' }, 200);
292
-
293
- // Text response
294
- c.text('Hello World', 200);
295
-
296
- // HTML response
297
- c.html('<h1>Hello</h1>', 200);
298
-
299
- // Redirect
300
- c.redirect('/new-location', 302);
301
-
302
- // File response
303
- c.file('./path/to/file.pdf', 'application/pdf');
304
-
305
- // Chain status
306
- c.status(201).json({ created: true });
307
- }
308
-
309
- // Request data
310
- handler : (c: AppContext) => {
311
- const params = c.params; // URL parameters
312
- const query = c.query; // Query string
313
- const body = c.body; // Request body
314
- const headers = c.headers; // Request headers
315
- const db = c.db; // Database instance
316
- const logger = c.logger; // Logger instance
317
- const requestId = c.requestId; // Unique request ID
318
- }
319
-
320
- // Headers
321
255
  handler : (c: AppContext) => {
322
- c.setHeader('X-Custom-Header', 'value');
323
- const auth = c.getHeader('Authorization');
324
- }
325
-
326
- // Cookies
327
- handler : (c: AppContext) => {
328
- c.setCookie('session', 'abc123', {
329
- maxAge: 3600,
330
- httpOnly: true,
331
- secure: true,
332
- sameSite: 'Strict'
333
- });
334
-
335
- const session = c.getCookie('session');
336
- c.deleteCookie('session');
256
+ // Request
257
+ c.params // URL parameters
258
+ c.query // Query string
259
+ c.body // Parsed body (JSON/form/multipart)
260
+ c.headers // Request headers
261
+ c.ip // Client IP
262
+ c.requestId // Unique request ID
263
+
264
+ // Resources
265
+ c.db // Database instance
266
+ c.logger // Logger instance
267
+
268
+ // Response
269
+ c.json({ data })
270
+ c.text('text')
271
+ c.html('HTML')
272
+ c.redirect('/path')
273
+ c.file('./file.pdf', 'application/pdf')
274
+ c.status(201).json({ created: true })
275
+
276
+ // Headers
277
+ c.setHeader('X-Custom', 'value')
278
+ c.getHeader('Authorization')
279
+
280
+ // Cookies
281
+ c.setCookie('session', 'token', { httpOnly: true })
282
+ c.getCookie('session')
283
+ c.deleteCookie('session')
337
284
  }
338
285
  ```
339
286
 
340
- <br>
341
-
342
- - ## Security Features
287
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
343
288
 
344
- - ### Rate Limiting
289
+ - ### Routes
345
290
 
346
291
  ```typescript
347
- const app = server({
348
- security: {
349
- rateLimit: {
350
- max: 100, // Max requests per window
351
- windowMs: 60000, // Time window in milliseconds
352
- keyGenerator: (c) => {
353
- // Custom key generation (default: IP address)
354
- return c.headers.get('x-api-key') || c.request.ip;
355
- },
356
- message: 'Rate limit exceeded'
357
- }
358
- }
359
- });
360
- ```
292
+ // Single method
293
+ { method : 'GET', path : '/users', handler }
361
294
 
362
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
295
+ // Multiple methods
296
+ { method : ['GET', 'POST'], path : '/api', handler }
363
297
 
364
- - ### CORS Configuration
298
+ // Dynamic parameters
299
+ { method : 'GET', path : '/users/:id', handler }
300
+ { method : 'GET', path : '/posts/:postId/comments/:commentId', handler }
365
301
 
366
- ```typescript
367
- const app = server({
368
- security: {
369
- cors: {
370
- // Allow specific origins
371
- origin: ['http://localhost:3000', 'https://example.com'],
372
-
373
- // Or use a function
374
- origin: (origin) => {
375
- return origin.endsWith('.example.com');
376
- },
377
-
378
- // Or allow all
379
- origin: '*',
380
-
381
- methods: ['GET', 'POST', 'PUT', 'DELETE'],
382
- allowedHeaders: ['Content-Type', 'Authorization'],
383
- credentials: true,
384
- maxAge: 86400 // 24 hours
385
- }
386
- }
387
- });
302
+ // Wildcards
303
+ { method : 'GET', path : '/files/*', handler }
388
304
  ```
389
305
 
390
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
306
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
391
307
 
392
- - ### CSRF Protection
308
+ - ### Database Operations
393
309
 
394
310
  ```typescript
395
- import { SecurityManager } from '@je-es/server';
396
-
397
- const security = new SecurityManager();
398
-
399
- // Generate CSRF token
400
- const token = security.generateCsrfToken('session-id', 3600000); // 1 hour TTL
401
-
402
- // Validate CSRF token
403
- const isValid = security.validateCsrfToken(token, 'session-id');
311
+ // CRUD
312
+ c.db!.all ('users')
313
+ c.db!.findById ('users', 1)
314
+ c.db!.find ('users', { role: 'admin' })
315
+ c.db!.insert ('users', { name: 'John' })
316
+ c.db!.update ('users', 1, { name: 'Jane' })
317
+ c.db!.delete ('users', 1)
318
+
319
+ // Multiple databases
320
+ const mainDb = app.db.get('default');
321
+ const cacheDb = app.db.get('cache');
404
322
  ```
405
323
 
406
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
324
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
407
325
 
408
- - ### Input Sanitization
326
+ - ### Dynamic Routes
409
327
 
410
328
  ```typescript
411
- import { SecurityManager } from '@je-es/server';
329
+ await app.start();
412
330
 
413
- const security = new SecurityManager();
331
+ // Add single route
332
+ app.addRoute({
333
+ method : 'POST',
334
+ path : '/dynamic',
335
+ handler : (c) => c.json({ dynamic: true })
336
+ });
414
337
 
415
- // Sanitize HTML
416
- const cleanHtml = security.sanitizeHtml('<script>alert("xss")</script>');
417
- // Output: &lt;script&gt;alert(&quot;xss&quot;)&lt;&#x2F;script&gt;
338
+ // Add multiple routes
339
+ app.addRoutes([
340
+ { method : 'GET', path : '/route1', handler },
341
+ { method : 'GET', path : '/route2', handler }
342
+ ]);
418
343
 
419
- // Sanitize SQL
420
- const cleanSql = security.sanitizeSql("'; DROP TABLE users--");
421
- // Output: ''; DROP TABLE users--
344
+ // Get all routes
345
+ const routes = app.getRoutes();
422
346
  ```
423
347
 
424
- <br>
348
+ <br>
425
349
 
426
- - ## Database Support
350
+ - ## Security
427
351
 
428
- - ### Single Database
352
+ - ### Rate Limiting
429
353
 
430
354
  ```typescript
431
- import { server } from '@je-es/server';
432
-
433
- const app = server({
434
- database: {
435
- connection: './my_app.db' // ✔ File-based SQLite - data persists!
436
- // or ':memory:' for in-memory database
355
+ security: {
356
+ rateLimit: {
357
+ max : 100,
358
+ windowMs : 60000,
359
+ keyGenerator : (c) => c.ip,
360
+ message : 'Too many requests'
437
361
  }
438
- });
439
-
440
- // Access in routes via c.db
441
- ```
442
-
443
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
444
-
445
- - ### Multiple Databases
446
-
447
- ```typescript
448
- import { server } from '@je-es/server';
449
-
450
- const app = server({
451
- database: [
452
- {
453
- name: 'default',
454
- connection: './main.db' // Main database file
455
- },
456
- {
457
- name: 'analytics',
458
- connection: './analytics.db' // Analytics database file
459
- }
460
- ],
461
- routes : [
462
- {
463
- method : 'GET',
464
- path : '/data',
465
- handler : (c) => {
466
- // Access default database
467
- const users = c.db.all('users');
468
-
469
- // Access named databases
470
- const mainDb = app.db.get('default');
471
- const analyticsDb = app.db.get('analytics');
472
-
473
- const mainData = mainDb.all('some_table');
474
- const analyticsData = analyticsDb.all('analytics_table');
475
-
476
- return c.json({ users, mainData, analyticsData });
477
- }
478
- }
479
- ]
480
- });
362
+ }
481
363
  ```
482
364
 
483
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
365
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
484
366
 
485
- - ### Schema Definition
367
+ - ### CORS
486
368
 
487
369
  ```typescript
488
- import {
489
- server,
490
- table,
491
- integer,
492
- text,
493
- real,
494
- blob,
495
- numeric,
496
- primaryKey,
497
- notNull,
498
- unique,
499
- defaultValue,
500
- references
501
- } from '@je-es/server';
502
-
503
- // Define products table
504
- const products = table('products', [
505
- primaryKey(integer('id'), true), // Auto-increment primary key
506
- notNull(text('name')),
507
- text('description'),
508
- notNull(real('price')),
509
- defaultValue(integer('stock'), 0)
510
- ]);
511
-
512
- // Define orders table with foreign key
513
- const orders = table('orders', [
514
- primaryKey(integer('id'), true),
515
- notNull(integer('product_id')),
516
- references(integer('product_id'), 'products', 'id'),
517
- notNull(integer('quantity')),
518
- defaultValue(text('status'), 'pending')
519
- ]);
520
-
521
- const app = server({
522
- database: {
523
- connection: './store.db',
524
- schema: { products, orders }
370
+ security: {
371
+ cors: {
372
+ origin : ['http://localhost:3000'],
373
+ // or: origin : '*',
374
+ // or: origin : (origin) => origin.endsWith('.example.com'),
375
+ methods : ['GET', 'POST', 'PUT', 'DELETE'],
376
+ credentials : true
525
377
  }
526
- });
527
- ```
528
-
529
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
530
-
531
- - ### Database Operations
532
-
533
- ```typescript
534
- import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
535
-
536
- const users = table('users', [
537
- primaryKey(integer('id'), true),
538
- notNull(text('name')),
539
- notNull(text('email')),
540
- integer('age')
541
- ]);
542
-
543
- const app = server({
544
- database: {
545
- connection: './app.db',
546
- schema: { users }
547
- },
548
- routes : [
549
- // Get all records
550
- {
551
- method : 'GET',
552
- path : '/users',
553
- handler : (c) => {
554
- const allUsers = c.db.all('users');
555
- return c.json(allUsers);
556
- }
557
- },
558
-
559
- // Find by ID
560
- {
561
- method : 'GET',
562
- path : '/users/:id',
563
- handler : (c) => {
564
- const user = c.db.findById('users', parseInt(c.params.id));
565
- if (!user) return c.status(404).json({ error: 'Not found' });
566
- return c.json(user);
567
- }
568
- },
569
-
570
- // Find with conditions
571
- {
572
- method : 'GET',
573
- path : '/users/search',
574
- handler : (c) => {
575
- const users = c.db.find('users', {
576
- name: c.query.name
577
- });
578
- return c.json(users);
579
- }
580
- },
581
-
582
- // Insert
583
- {
584
- method : 'POST',
585
- path : '/users',
586
- handler : (c) => {
587
- const newUser = c.db.insert('users', c.body);
588
- return c.json(newUser);
589
- }
590
- },
591
-
592
- // Update
593
- {
594
- method : 'PUT',
595
- path : '/users/:id',
596
- handler : (c) => {
597
- const updated = c.db.update(
598
- 'users',
599
- parseInt(c.params.id),
600
- c.body
601
- );
602
- if (!updated) return c.status(404).json({ error: 'Not found' });
603
- return c.json(updated);
604
- }
605
- },
606
-
607
- // Delete
608
- {
609
- method : 'DELETE',
610
- path : '/users/:id',
611
- handler : (c) => {
612
- c.db.delete('users', parseInt(c.params.id));
613
- return c.json({ deleted: true });
614
- }
615
- }
616
- ]
617
- });
378
+ }
618
379
  ```
619
380
 
620
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
381
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
621
382
 
622
- - ### Query Builder
383
+ - ### Input Sanitization
623
384
 
624
385
  ```typescript
625
- const app = server({
626
- database: {
627
- connection: './app.db',
628
- schema: { users }
629
- },
630
- routes : [
631
- {
632
- method : 'GET',
633
- path : '/advanced-search',
634
- handler : (c) => {
635
- // Complex queries with query builder
636
- const results = c.db.query()
637
- .select(['name', 'email', 'age'])
638
- .from('users')
639
- .where({
640
- column: 'age',
641
- operator: '>=',
642
- value: 18
643
- })
644
- .and({
645
- column: 'name',
646
- operator: 'LIKE',
647
- value: '%John%'
648
- })
649
- .orderBy('age', 'DESC')
650
- .limit(10)
651
- .offset(0)
652
- .execute();
653
-
654
- return c.json(results);
655
- }
656
- },
657
-
658
- // Multiple where conditions
659
- {
660
- method : 'GET',
661
- path : '/filter',
662
- handler : (c) => {
663
- const users = c.db.query()
664
- .select()
665
- .from('users')
666
- .where([
667
- { column: 'age', operator: '>', value: 25 },
668
- { column: 'age', operator: '<', value: 50 }
669
- ])
670
- .execute();
671
-
672
- return c.json(users);
673
- }
674
- },
675
-
676
- // OR conditions
677
- {
678
- method : 'GET',
679
- path : '/or-search',
680
- handler : (c) => {
681
- const users = c.db.query()
682
- .select()
683
- .from('users')
684
- .where({ column: 'name', operator: '=', value: 'John' })
685
- .or({ column: 'name', operator: '=', value: 'Jane' })
686
- .execute();
687
-
688
- return c.json(users);
689
- }
690
- },
386
+ import { SecurityManager } from '@je-es/server';
691
387
 
692
- // Get single result
693
- {
694
- method : 'GET',
695
- path : '/first-user',
696
- handler : (c) => {
697
- const user = c.db.query()
698
- .select()
699
- .from('users')
700
- .limit(1)
701
- .executeOne();
388
+ const security = new SecurityManager();
702
389
 
703
- return c.json(user);
704
- }
705
- }
706
- ]
707
- });
390
+ security.sanitizeHtml('xss');
391
+ security.sanitizeSql("'; DROP TABLE users--");
708
392
  ```
709
393
 
710
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
394
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
711
395
 
712
- - ### Transactions
396
+ - ### CSRF Protection
713
397
 
714
398
  ```typescript
715
- const app = server({
716
- database: {
717
- connection: './app.db',
718
- schema: { users, orders }
719
- },
720
- routes : [
721
- {
722
- method : 'POST',
723
- path : '/place-order',
724
- handler : (c) => {
725
- try {
726
- c.db.transaction((db) => {
727
- // Insert order
728
- const order = db.insert('orders', {
729
- product_id: c.body.productId,
730
- quantity: c.body.quantity
731
- });
732
-
733
- // Update product stock
734
- const product = db.findById('products', c.body.productId);
735
- db.update('products', c.body.productId, {
736
- stock: product.stock - c.body.quantity
737
- });
738
- });
739
-
740
- return c.json({ success: true });
741
- } catch (error) {
742
- return c.status(500).json({
743
- error: 'Transaction failed'
744
- });
745
- }
746
- }
747
- }
748
- ]
749
- });
399
+ const security = new SecurityManager();
400
+ const token = security.generateCsrfToken('session-id');
401
+ const valid = security.validateCsrfToken(token, 'session-id');
750
402
  ```
751
403
 
752
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
753
-
754
- - ### Raw SQL
755
-
756
- ```typescript
757
- const app = server({
758
- database: {
759
- connection: './app.db',
760
- schema: { users }
761
- },
762
- routes : [
763
- {
764
- method : 'GET',
765
- path : '/custom-query',
766
- handler : (c) => {
767
- // Execute raw SQL
768
- const results = c.db.raw(
769
- 'SELECT * FROM users WHERE age > ? AND name LIKE ?',
770
- [25, '%John%']
771
- );
772
-
773
- return c.json(results);
774
- }
775
- },
404
+ <br>
776
405
 
777
- {
778
- method : 'GET',
779
- path : '/single-result',
780
- handler : (c) => {
781
- // Get single row
782
- const user = c.db.rawOne(
783
- 'SELECT * FROM users WHERE id = ?',
784
- [1]
785
- );
406
+ - ## Error Handling
786
407
 
787
- return c.json(user);
788
- }
789
- },
408
+ ```typescript
409
+ import {
410
+ AppError, // Custom errors
411
+ ValidationError, // 400
412
+ DatabaseError, // 500
413
+ TimeoutError, // 408
414
+ RateLimitError // 429
415
+ } from '@je-es/server';
790
416
 
791
- {
792
- method : 'POST',
793
- path : '/execute-sql',
794
- handler : (c) => {
795
- // Execute without return
796
- c.db.exec('DELETE FROM users WHERE age < 18');
417
+ handler : (c) => {
418
+ if (!c.body?.email) {
419
+ throw new ValidationError('Email required');
420
+ }
797
421
 
798
- return c.json({ success: true });
799
- }
800
- }
801
- ]
802
- });
803
- ```
422
+ if (invalid) {
423
+ throw new AppError('Invalid data', 400, 'INVALID_DATA');
424
+ }
804
425
 
805
- <br>
426
+ return c.json({ success: true });
427
+ }
428
+ ```
806
429
 
807
- - ## Advanced Features
430
+ <br>
808
431
 
809
- - ### Logging
432
+ - ## Built-in Endpoints
810
433
 
811
- ```typescript
812
- import { server, Logger } from '@je-es/server';
434
+ ```typescript
435
+ // Health check
436
+ GET /health
437
+ // Response: { status, timestamp, uptime, activeRequests }
813
438
 
814
- // Enable logging
815
- const app = server({
816
- logging: {
817
- level: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'fatal'
818
- pretty: true // Pretty print for development
819
- }
820
- });
439
+ // Readiness check
440
+ GET /readiness
441
+ // Response: { ready, checks: { database, activeRequests }, timestamp }
442
+ ```
821
443
 
822
- // Use logger in routes
823
- const route = {
824
- method : 'GET',
825
- path : '/test',
826
- handler : (c) => {
827
- c.logger?.info({ userId: 123 }, 'User accessed endpoint');
828
- c.logger?.warn({ attempt: 3 }, 'Suspicious activity');
829
- c.logger?.error({ error: 'DB connection failed' }, 'Database error');
830
- return c.json({ ok: true });
831
- }
832
- };
833
- ```
444
+ <br>
834
445
 
835
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
446
+ - ## Advanced
836
447
 
837
448
  - ### Cookie Management
838
449
 
839
450
  ```typescript
840
- const app = server({
841
- routes : [
842
- {
843
- method : 'POST',
844
- path : '/login',
845
- handler : (c) => {
846
- // Set cookie with options
847
- c.setCookie('session', 'user-token-123', {
848
- maxAge: 3600, // 1 hour
849
- expires: new Date('2025-12-31'),
850
- path : '/',
851
- domain: 'example.com',
852
- secure: true, // HTTPS only
853
- httpOnly: true, // No JavaScript access
854
- sameSite: 'Strict' // CSRF protection
855
- });
856
-
857
- return c.json({ loggedIn: true });
858
- }
859
- },
860
- {
861
- method : 'GET',
862
- path : '/profile',
863
- handler : (c) => {
864
- const session = c.getCookie('session');
865
- if (!session) {
866
- return c.status(401).json({ error: 'Unauthorized' });
867
- }
868
- return c.json({ session });
869
- }
870
- },
871
- {
872
- method : 'POST',
873
- path : '/logout',
874
- handler : (c) => {
875
- c.deleteCookie('session');
876
- return c.json({ loggedOut: true });
877
- }
878
- }
879
- ]
451
+ c.setCookie('session', 'token', {
452
+ maxAge : 3600,
453
+ httpOnly : true,
454
+ secure : true,
455
+ sameSite : 'Strict',
456
+ path : '/',
457
+ domain : 'example.com'
880
458
  });
881
459
  ```
882
460
 
883
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
461
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
884
462
 
885
- - ### Dynamic Routing
463
+ - ### Static File Options
886
464
 
887
465
  ```typescript
888
- const app = server({
889
- routes : [
890
- // Simple parameter
891
- {
892
- method : 'GET',
893
- path : '/users/:id',
894
- handler : (c) => c.json({ userId: c.params.id })
895
- },
896
-
897
- // Multiple parameters
898
- {
899
- method : 'GET',
900
- path : '/posts/:postId/comments/:commentId',
901
- handler : (c) => c.json({
902
- postId: c.params.postId,
903
- commentId: c.params.commentId
904
- })
905
- },
906
-
907
- // Complex patterns
908
- {
909
- method : 'GET',
910
- path : '/api/:version/:resource',
911
- handler : (c) => c.json({
912
- version: c.params.version,
913
- resource: c.params.resource
914
- })
915
- }
916
- ]
917
- });
918
- ```
919
-
920
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
921
-
922
- - ### Health Checks
923
-
924
- ```typescript
925
- // Built-in health endpoints are automatically available
926
-
927
- // GET /health
928
- // Response:
929
- {
930
- status: 'healthy',
931
- timestamp: '2025-11-28T10:00:00.000Z',
932
- uptime: 3600,
933
- activeRequests: 5
934
- }
935
-
936
- // GET /readiness
937
- // Response:
938
- {
939
- ready: true,
940
- checks: {
941
- database: 'connected', // or 'not configured'
942
- activeRequests: 5
943
- },
944
- timestamp: '2025-11-28T10:00:00.000Z'
466
+ static: {
467
+ path : '/public',
468
+ directory : './public',
469
+ maxAge : 3600,
470
+ index : ['index.html'],
471
+ dotfiles : 'deny', // 'allow' | 'deny' | 'ignore'
472
+ etag : true,
473
+ lastModified : true,
474
+ immutable : false,
475
+ extensions : ['html', 'htm'],
476
+ setHeaders : (ctx, path) => {
477
+ ctx.setHeader('X-Custom', 'value');
478
+ }
945
479
  }
946
480
  ```
947
481
 
948
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
482
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
949
483
 
950
484
  - ### Graceful Shutdown
951
485
 
952
486
  ```typescript
953
487
  const app = server({
954
- gracefulShutdownTimeout: 10000, // 10 seconds
488
+ gracefulShutdownTimeout: 10000,
955
489
  onShutdown: async () => {
956
- console.log('Cleaning up resources...');
957
- // Close external connections, flush logs, etc.
490
+ // Cleanup
958
491
  }
959
492
  });
960
493
 
961
- await app.start();
962
-
963
- // Handle signals
964
494
  process.on('SIGTERM', async () => {
965
495
  await app.stop();
966
496
  process.exit(0);
967
497
  });
968
-
969
- process.on('SIGINT', async () => {
970
- await app.stop();
971
- process.exit(0);
972
- });
973
498
  ```
974
499
 
975
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
500
+ <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
976
501
 
977
- - ### Dynamic Routes
502
+ - ### Logging
978
503
 
979
504
  ```typescript
980
- const app = server({
981
- routes : [
982
- {
983
- method : 'GET',
984
- path : '/initial',
985
- handler : (c) => c.json({ route: 'initial' })
986
- }
987
- ]
988
- });
989
-
990
- await app.start();
991
-
992
- // Add routes after server starts
993
- app.addRoute({
994
- method : 'POST',
995
- path : '/dynamic',
996
- handler : (c) => c.json({ route: 'dynamic', body: c.body })
997
- });
998
-
999
- // Get all registered routes
1000
- const routes = app.getRoutes();
1001
- console.log(routes);
505
+ handler : (c) => {
506
+ c.logger?.info( { userId: 123 }, 'User action');
507
+ c.logger?.warn( { attempt: 3 }, 'Warning');
508
+ c.logger?.error({ error: 'msg' }, 'Error');
509
+ }
1002
510
  ```
1003
511
 
1004
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
1005
-
1006
- - ### Request Timeout
1007
-
1008
- ```typescript
1009
- const app = server({
1010
- requestTimeout : 5000, // 5 seconds
1011
- routes : [
1012
- {
1013
- method : 'GET',
1014
- path : '/slow',
1015
- handler : async (c) => {
1016
- // If this takes more than 5 seconds, request will timeout
1017
- await someSlowOperation();
1018
- return c.json({ done: true });
512
+ <br>
513
+
514
+ - ## Complete Example
515
+
516
+ ```typescript
517
+ import { server, table, integer, text, primaryKey, notNull } from '@je-es/server';
518
+
519
+ const users = table('users', [
520
+ primaryKey(integer('id'), true),
521
+ notNull(text('name')),
522
+ notNull(text('email'))
523
+ ]);
524
+
525
+ const app = server({
526
+ port : 3000,
527
+ logging : { level: 'info', pretty: true },
528
+ database : { connection: './app.db', schema: { users } },
529
+ security : {
530
+ rateLimit : { max: 100, windowMs: 60000 },
531
+ cors : { origin: ['http://localhost:3000'] }
532
+ },
533
+ static : { path: '/public', directory: './public' },
534
+ routes : [
535
+ {
536
+ method : 'GET',
537
+ path : '/api/users',
538
+ handler : (c) => c.json(c.db!.all('users'))
539
+ },
540
+ {
541
+ method : 'POST',
542
+ path : '/api/users',
543
+ handler : (c) => {
544
+ if (!c.body?.name || !c.body?.email) {
545
+ throw new ValidationError('Name and email required');
1019
546
  }
547
+ const user = c.db!.insert('users', c.body);
548
+ return c.status(201).json(user);
1020
549
  }
1021
- ]
1022
- });
1023
- ```
1024
-
1025
- <div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> <br> </div>
1026
-
1027
- - ### Custom Error Handling
1028
-
1029
- ```typescript
1030
- import { AppError, ValidationError } from '@je-es/server';
1031
-
1032
- const app = server({
1033
- routes : [
1034
- {
1035
- method : 'POST',
1036
- path : '/validate',
1037
- handler : (c) => {
1038
- if (!c.body?.email) {
1039
- throw new ValidationError('Email is required');
1040
- }
1041
-
1042
- if (!c.body.email.includes('@')) {
1043
- throw new AppError('Invalid email format', 400, 'INVALID_EMAIL');
1044
- }
1045
-
1046
- return c.json({ valid: true });
1047
- }
550
+ },
551
+ {
552
+ method : 'GET',
553
+ path : '/api/users/:id',
554
+ handler : (c) => {
555
+ const user = c.db!.findById('users', parseInt(c.params.id));
556
+ return user ? c.json(user) : c.status(404).json({ error: 'Not found' });
1048
557
  }
1049
- ]
1050
- });
558
+ }
559
+ ]
560
+ });
1051
561
 
1052
- // Error responses are automatically formatted:
1053
- {
1054
- error : 'Email is required',
1055
- code : 'VALIDATION_ERROR',
1056
- requestId : 'unique-request-id'
1057
- }
1058
- ```
562
+ await app.start();
563
+ ```
1059
564
 
1060
565
  <!-- ╚═════════════════════════════════════════════════════════════════╝ -->
1061
566