te.js 2.0.3 → 2.1.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.
- package/README.md +197 -187
- package/auto-docs/analysis/handler-analyzer.js +58 -58
- package/auto-docs/analysis/source-resolver.js +101 -101
- package/auto-docs/constants.js +37 -37
- package/auto-docs/docs-llm/index.js +7 -0
- package/auto-docs/{llm → docs-llm}/prompts.js +222 -222
- package/auto-docs/{llm → docs-llm}/provider.js +132 -187
- package/auto-docs/index.js +146 -146
- package/auto-docs/openapi/endpoint-processor.js +277 -277
- package/auto-docs/openapi/generator.js +107 -107
- package/auto-docs/openapi/level3.js +131 -131
- package/auto-docs/openapi/spec-builders.js +244 -244
- package/auto-docs/ui/docs-ui.js +186 -186
- package/auto-docs/utils/logger.js +17 -17
- package/auto-docs/utils/strip-usage.js +10 -10
- package/cli/docs-command.js +315 -315
- package/cli/fly-command.js +71 -71
- package/cli/index.js +56 -56
- package/database/index.js +165 -165
- package/database/mongodb.js +146 -146
- package/database/redis.js +201 -201
- package/docs/README.md +36 -36
- package/docs/ammo.md +362 -362
- package/docs/api-reference.md +490 -489
- package/docs/auto-docs.md +216 -215
- package/docs/cli.md +152 -152
- package/docs/configuration.md +275 -233
- package/docs/database.md +390 -391
- package/docs/error-handling.md +438 -417
- package/docs/file-uploads.md +333 -334
- package/docs/getting-started.md +214 -215
- package/docs/middleware.md +355 -356
- package/docs/rate-limiting.md +393 -394
- package/docs/routing.md +302 -302
- package/package.json +62 -62
- package/rate-limit/algorithms/fixed-window.js +141 -141
- package/rate-limit/algorithms/sliding-window.js +147 -147
- package/rate-limit/algorithms/token-bucket.js +115 -115
- package/rate-limit/base.js +165 -165
- package/rate-limit/index.js +147 -147
- package/rate-limit/storage/base.js +104 -104
- package/rate-limit/storage/memory.js +101 -101
- package/rate-limit/storage/redis.js +88 -88
- package/server/ammo/body-parser.js +220 -220
- package/server/ammo/dispatch-helper.js +103 -103
- package/server/ammo/enhancer.js +57 -57
- package/server/ammo.js +454 -356
- package/server/endpoint.js +97 -74
- package/server/error.js +9 -9
- package/server/errors/code-context.js +125 -0
- package/server/errors/llm-error-service.js +140 -0
- package/server/files/helper.js +33 -33
- package/server/files/uploader.js +143 -143
- package/server/handler.js +158 -113
- package/server/target.js +185 -175
- package/server/targets/middleware-validator.js +22 -22
- package/server/targets/path-validator.js +21 -21
- package/server/targets/registry.js +160 -160
- package/server/targets/shoot-validator.js +21 -21
- package/te.js +428 -363
- package/utils/auto-register.js +17 -17
- package/utils/configuration.js +64 -64
- package/utils/errors-llm-config.js +84 -0
- package/utils/request-logger.js +43 -43
- package/utils/status-codes.js +82 -82
- package/utils/tejas-entrypoint-html.js +18 -18
- package/auto-docs/llm/index.js +0 -6
- package/auto-docs/llm/parse.js +0 -88
package/docs/database.md
CHANGED
|
@@ -1,391 +1,390 @@
|
|
|
1
|
-
# Database Integration
|
|
2
|
-
|
|
3
|
-
Tejas provides built-in support for **MongoDB** and **Redis** databases through a centralized `DatabaseManager`.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
### Redis
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
import Tejas from 'te.js';
|
|
11
|
-
|
|
12
|
-
const app = new Tejas();
|
|
13
|
-
|
|
14
|
-
app.takeoff({
|
|
15
|
-
withRedis: { url: 'redis://localhost:6379' }
|
|
16
|
-
});
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
### MongoDB
|
|
20
|
-
|
|
21
|
-
```javascript
|
|
22
|
-
app.takeoff({
|
|
23
|
-
withMongo: { uri: 'mongodb://localhost:27017/myapp' }
|
|
24
|
-
});
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Both Together
|
|
28
|
-
|
|
29
|
-
```javascript
|
|
30
|
-
app.takeoff({
|
|
31
|
-
withRedis: { url: 'redis://localhost:6379' },
|
|
32
|
-
withMongo: { uri: 'mongodb://localhost:27017/myapp' }
|
|
33
|
-
});
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
> **Auto-install:** Tejas automatically installs the `redis` or `mongoose` npm packages on first use if they are not already in your `node_modules`. No manual `npm install` is required for database drivers.
|
|
37
|
-
|
|
38
|
-
## Redis Configuration
|
|
39
|
-
|
|
40
|
-
### Basic Connection
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
app.withRedis({
|
|
44
|
-
url: 'redis://localhost:6379'
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### With Authentication
|
|
49
|
-
|
|
50
|
-
```javascript
|
|
51
|
-
app.withRedis({
|
|
52
|
-
url: 'redis://username:password@hostname:6379'
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Or using socket options
|
|
56
|
-
app.withRedis({
|
|
57
|
-
socket: {
|
|
58
|
-
host: 'localhost',
|
|
59
|
-
port: 6379
|
|
60
|
-
},
|
|
61
|
-
password: 'your-password'
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### TLS Connection
|
|
66
|
-
|
|
67
|
-
```javascript
|
|
68
|
-
app.withRedis({
|
|
69
|
-
socket: {
|
|
70
|
-
host: 'your-redis-host.com',
|
|
71
|
-
port: 6379,
|
|
72
|
-
tls: true
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Redis Cluster
|
|
78
|
-
|
|
79
|
-
```javascript
|
|
80
|
-
app.withRedis({
|
|
81
|
-
isCluster: true,
|
|
82
|
-
url: 'redis://node1:6379'
|
|
83
|
-
});
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### All Options
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
app.withRedis({
|
|
90
|
-
url: 'redis://localhost:6379', // Connection URL
|
|
91
|
-
isCluster: false, // Use Redis Cluster
|
|
92
|
-
socket: {
|
|
93
|
-
host: 'localhost',
|
|
94
|
-
port: 6379,
|
|
95
|
-
tls: false
|
|
96
|
-
},
|
|
97
|
-
password: 'secret', // Redis password
|
|
98
|
-
database: 0, // Database number
|
|
99
|
-
// ... any other node-redis options
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## MongoDB Configuration
|
|
104
|
-
|
|
105
|
-
### Basic Connection
|
|
106
|
-
|
|
107
|
-
```javascript
|
|
108
|
-
app.withMongo({
|
|
109
|
-
uri: 'mongodb://localhost:27017/myapp'
|
|
110
|
-
});
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### With Options
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
app.withMongo({
|
|
117
|
-
uri: 'mongodb://localhost:27017/myapp',
|
|
118
|
-
options: {
|
|
119
|
-
maxPoolSize: 10,
|
|
120
|
-
serverSelectionTimeoutMS: 5000,
|
|
121
|
-
socketTimeoutMS: 45000
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### MongoDB Atlas
|
|
127
|
-
|
|
128
|
-
```javascript
|
|
129
|
-
app.withMongo({
|
|
130
|
-
uri: 'mongodb+srv://username:password@cluster.mongodb.net/myapp'
|
|
131
|
-
});
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Replica Set
|
|
135
|
-
|
|
136
|
-
```javascript
|
|
137
|
-
app.withMongo({
|
|
138
|
-
uri: 'mongodb://host1:27017,host2:27017,host3:27017/myapp?replicaSet=myReplicaSet'
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Using Database Connections
|
|
143
|
-
|
|
144
|
-
### Getting Connections
|
|
145
|
-
|
|
146
|
-
Import the database manager to access connections:
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
import dbManager from 'te.js/database/index.js';
|
|
150
|
-
|
|
151
|
-
// Get Redis client
|
|
152
|
-
const redis = dbManager.getConnection('redis');
|
|
153
|
-
|
|
154
|
-
// Get MongoDB client
|
|
155
|
-
const mongo = dbManager.getConnection('mongodb');
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### In Route Handlers
|
|
159
|
-
|
|
160
|
-
```javascript
|
|
161
|
-
import { Target } from 'te.js';
|
|
162
|
-
import dbManager from 'te.js/database/index.js';
|
|
163
|
-
|
|
164
|
-
const cache = new Target('/cache');
|
|
165
|
-
|
|
166
|
-
cache.register('/get/:key', async (ammo) => {
|
|
167
|
-
const redis = dbManager.getConnection('redis');
|
|
168
|
-
const { key } = ammo.payload;
|
|
169
|
-
|
|
170
|
-
const value = await redis.get(key);
|
|
171
|
-
|
|
172
|
-
if (!value) {
|
|
173
|
-
return ammo.fire(404, { error: 'Key not found' });
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
ammo.fire({ key, value });
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
cache.register('/set', async (ammo) => {
|
|
180
|
-
if (!ammo.POST) return ammo.notAllowed();
|
|
181
|
-
|
|
182
|
-
const redis = dbManager.getConnection('redis');
|
|
183
|
-
const { key, value, ttl } = ammo.payload;
|
|
184
|
-
|
|
185
|
-
if (ttl) {
|
|
186
|
-
await redis.setEx(key, ttl, value);
|
|
187
|
-
} else {
|
|
188
|
-
await redis.set(key, value);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
ammo.fire(201, { message: 'Cached successfully' });
|
|
192
|
-
});
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### MongoDB Example
|
|
196
|
-
|
|
197
|
-
```javascript
|
|
198
|
-
import { Target, TejError } from 'te.js';
|
|
199
|
-
import dbManager from 'te.js/database/index.js';
|
|
200
|
-
|
|
201
|
-
const users = new Target('/users');
|
|
202
|
-
|
|
203
|
-
users.register('/', async (ammo) => {
|
|
204
|
-
const mongo = dbManager.getConnection('mongodb');
|
|
205
|
-
const db = mongo.db('myapp');
|
|
206
|
-
const collection = db.collection('users');
|
|
207
|
-
|
|
208
|
-
if (ammo.GET) {
|
|
209
|
-
const users = await collection.find({}).toArray();
|
|
210
|
-
return ammo.fire(users);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (ammo.POST) {
|
|
214
|
-
const { name, email } = ammo.payload;
|
|
215
|
-
const result = await collection.insertOne({ name, email, createdAt: new Date() });
|
|
216
|
-
return ammo.fire(201, { id: result.insertedId, name, email });
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
ammo.notAllowed();
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
users.register('/:id', async (ammo) => {
|
|
223
|
-
const mongo = dbManager.getConnection('mongodb');
|
|
224
|
-
const db = mongo.db('myapp');
|
|
225
|
-
const collection = db.collection('users');
|
|
226
|
-
|
|
227
|
-
const { id } = ammo.payload;
|
|
228
|
-
const { ObjectId } = await import('mongodb');
|
|
229
|
-
|
|
230
|
-
const user = await collection.findOne({ _id: new ObjectId(id) });
|
|
231
|
-
|
|
232
|
-
if (!user) {
|
|
233
|
-
throw new TejError(404, 'User not found');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
ammo.fire(user);
|
|
237
|
-
});
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## Database Manager API
|
|
241
|
-
|
|
242
|
-
Access the database manager directly for advanced usage:
|
|
243
|
-
|
|
244
|
-
```javascript
|
|
245
|
-
import dbManager from 'te.js/database/index.js';
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Check Connection Status
|
|
249
|
-
|
|
250
|
-
```javascript
|
|
251
|
-
const status = dbManager.hasConnection('redis', {});
|
|
252
|
-
// Returns: { exists: boolean, initializing: boolean }
|
|
253
|
-
|
|
254
|
-
if (status.exists) {
|
|
255
|
-
const redis = dbManager.getConnection('redis');
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
The `initializing` flag is `true` while the connection is being established.
|
|
260
|
-
|
|
261
|
-
### Get All Active Connections
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
const connections = dbManager.getActiveConnections();
|
|
265
|
-
// Returns a Map: { 'redis' => { type, client, config }, 'mongodb' => { type, client, config } }
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Close Connections
|
|
269
|
-
|
|
270
|
-
```javascript
|
|
271
|
-
// Close a specific connection
|
|
272
|
-
await dbManager.closeConnection('redis');
|
|
273
|
-
|
|
274
|
-
// Close all connections
|
|
275
|
-
await dbManager.closeAllConnections();
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
## Caching Pattern
|
|
279
|
-
|
|
280
|
-
A common pattern using Redis for caching:
|
|
281
|
-
|
|
282
|
-
```javascript
|
|
283
|
-
import { Target } from 'te.js';
|
|
284
|
-
import dbManager from 'te.js/database/index.js';
|
|
285
|
-
|
|
286
|
-
const api = new Target('/api');
|
|
287
|
-
|
|
288
|
-
// Cache middleware
|
|
289
|
-
const withCache = (ttl = 60) => async (ammo, next) => {
|
|
290
|
-
if (!ammo.GET) return next();
|
|
291
|
-
|
|
292
|
-
const redis = dbManager.getConnection('redis');
|
|
293
|
-
const cacheKey = `cache:${ammo.endpoint}`;
|
|
294
|
-
|
|
295
|
-
// Try cache first
|
|
296
|
-
const cached = await redis.get(cacheKey);
|
|
297
|
-
if (cached) {
|
|
298
|
-
ammo.res.setHeader('X-Cache', 'HIT');
|
|
299
|
-
return ammo.fire(JSON.parse(cached));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Store original fire to intercept
|
|
303
|
-
const originalFire = ammo.fire.bind(ammo);
|
|
304
|
-
ammo.fire = async (...args) => {
|
|
305
|
-
const data = args[0];
|
|
306
|
-
if (typeof data === 'object') {
|
|
307
|
-
await redis.setEx(cacheKey, ttl, JSON.stringify(data));
|
|
308
|
-
}
|
|
309
|
-
ammo.res.setHeader('X-Cache', 'MISS');
|
|
310
|
-
originalFire(...args);
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
next();
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
api.register('/expensive-data', withCache(300), async (ammo) => {
|
|
317
|
-
// This expensive operation result will be cached for 5 minutes
|
|
318
|
-
const data = await expensiveOperation();
|
|
319
|
-
ammo.fire(data);
|
|
320
|
-
});
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## Session Storage with Redis
|
|
324
|
-
|
|
325
|
-
```javascript
|
|
326
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
327
|
-
import dbManager from 'te.js/database/index.js';
|
|
328
|
-
|
|
329
|
-
const sessionMiddleware = async (ammo, next) => {
|
|
330
|
-
const redis = dbManager.getConnection('redis');
|
|
331
|
-
let sessionId = ammo.headers['x-session-id'];
|
|
332
|
-
|
|
333
|
-
if (!sessionId) {
|
|
334
|
-
sessionId = uuidv4();
|
|
335
|
-
ammo.res.setHeader('X-Session-ID', sessionId);
|
|
336
|
-
ammo.session = {};
|
|
337
|
-
} else {
|
|
338
|
-
const sessionData = await redis.get(`session:${sessionId}`);
|
|
339
|
-
ammo.session = sessionData ? JSON.parse(sessionData) : {};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Save session after response
|
|
343
|
-
const originalFire = ammo.fire.bind(ammo);
|
|
344
|
-
ammo.fire = async (...args) => {
|
|
345
|
-
await redis.setEx(
|
|
346
|
-
`session:${sessionId}`,
|
|
347
|
-
3600, // 1 hour TTL
|
|
348
|
-
JSON.stringify(ammo.session)
|
|
349
|
-
);
|
|
350
|
-
originalFire(...args);
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
next();
|
|
354
|
-
};
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## Connection Events
|
|
358
|
-
|
|
359
|
-
The underlying clients emit events you can listen to:
|
|
360
|
-
|
|
361
|
-
```javascript
|
|
362
|
-
const redis = dbManager.getConnection('redis');
|
|
363
|
-
|
|
364
|
-
redis.on('error', (err) => {
|
|
365
|
-
console.error('Redis error:', err);
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
redis.on('connect', () => {
|
|
369
|
-
console.log('Redis connected');
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
redis.on('reconnecting', () => {
|
|
373
|
-
console.log('Redis reconnecting...');
|
|
374
|
-
});
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
## Best Practices
|
|
378
|
-
|
|
379
|
-
1. **Initialize early** — Set up connections before `takeoff()`
|
|
380
|
-
2. **Handle errors** — Always wrap database operations in try/catch
|
|
381
|
-
3. **Use connection pooling** — MongoDB handles this automatically
|
|
382
|
-
4. **Close on shutdown** — Clean up connections when app terminates
|
|
383
|
-
|
|
384
|
-
```javascript
|
|
385
|
-
// Graceful shutdown
|
|
386
|
-
process.on('SIGTERM', async () => {
|
|
387
|
-
await dbManager.closeAllConnections();
|
|
388
|
-
process.exit(0);
|
|
389
|
-
});
|
|
390
|
-
```
|
|
391
|
-
|
|
1
|
+
# Database Integration
|
|
2
|
+
|
|
3
|
+
Tejas provides built-in support for **MongoDB** and **Redis** databases through a centralized `DatabaseManager`.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Redis
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import Tejas from 'te.js';
|
|
11
|
+
|
|
12
|
+
const app = new Tejas();
|
|
13
|
+
|
|
14
|
+
app.takeoff({
|
|
15
|
+
withRedis: { url: 'redis://localhost:6379' }
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### MongoDB
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
app.takeoff({
|
|
23
|
+
withMongo: { uri: 'mongodb://localhost:27017/myapp' }
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Both Together
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
app.takeoff({
|
|
31
|
+
withRedis: { url: 'redis://localhost:6379' },
|
|
32
|
+
withMongo: { uri: 'mongodb://localhost:27017/myapp' }
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> **Auto-install:** Tejas automatically installs the `redis` or `mongoose` npm packages on first use if they are not already in your `node_modules`. No manual `npm install` is required for database drivers.
|
|
37
|
+
|
|
38
|
+
## Redis Configuration
|
|
39
|
+
|
|
40
|
+
### Basic Connection
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
app.withRedis({
|
|
44
|
+
url: 'redis://localhost:6379'
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### With Authentication
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
app.withRedis({
|
|
52
|
+
url: 'redis://username:password@hostname:6379'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Or using socket options
|
|
56
|
+
app.withRedis({
|
|
57
|
+
socket: {
|
|
58
|
+
host: 'localhost',
|
|
59
|
+
port: 6379
|
|
60
|
+
},
|
|
61
|
+
password: 'your-password'
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### TLS Connection
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
app.withRedis({
|
|
69
|
+
socket: {
|
|
70
|
+
host: 'your-redis-host.com',
|
|
71
|
+
port: 6379,
|
|
72
|
+
tls: true
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Redis Cluster
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
app.withRedis({
|
|
81
|
+
isCluster: true,
|
|
82
|
+
url: 'redis://node1:6379'
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### All Options
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
app.withRedis({
|
|
90
|
+
url: 'redis://localhost:6379', // Connection URL
|
|
91
|
+
isCluster: false, // Use Redis Cluster
|
|
92
|
+
socket: {
|
|
93
|
+
host: 'localhost',
|
|
94
|
+
port: 6379,
|
|
95
|
+
tls: false
|
|
96
|
+
},
|
|
97
|
+
password: 'secret', // Redis password
|
|
98
|
+
database: 0, // Database number
|
|
99
|
+
// ... any other node-redis options
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## MongoDB Configuration
|
|
104
|
+
|
|
105
|
+
### Basic Connection
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
app.withMongo({
|
|
109
|
+
uri: 'mongodb://localhost:27017/myapp'
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### With Options
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
app.withMongo({
|
|
117
|
+
uri: 'mongodb://localhost:27017/myapp',
|
|
118
|
+
options: {
|
|
119
|
+
maxPoolSize: 10,
|
|
120
|
+
serverSelectionTimeoutMS: 5000,
|
|
121
|
+
socketTimeoutMS: 45000
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### MongoDB Atlas
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
app.withMongo({
|
|
130
|
+
uri: 'mongodb+srv://username:password@cluster.mongodb.net/myapp'
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Replica Set
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
app.withMongo({
|
|
138
|
+
uri: 'mongodb://host1:27017,host2:27017,host3:27017/myapp?replicaSet=myReplicaSet'
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Using Database Connections
|
|
143
|
+
|
|
144
|
+
### Getting Connections
|
|
145
|
+
|
|
146
|
+
Import the database manager to access connections:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
import dbManager from 'te.js/database/index.js';
|
|
150
|
+
|
|
151
|
+
// Get Redis client
|
|
152
|
+
const redis = dbManager.getConnection('redis');
|
|
153
|
+
|
|
154
|
+
// Get MongoDB client
|
|
155
|
+
const mongo = dbManager.getConnection('mongodb');
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### In Route Handlers
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
import { Target } from 'te.js';
|
|
162
|
+
import dbManager from 'te.js/database/index.js';
|
|
163
|
+
|
|
164
|
+
const cache = new Target('/cache');
|
|
165
|
+
|
|
166
|
+
cache.register('/get/:key', async (ammo) => {
|
|
167
|
+
const redis = dbManager.getConnection('redis');
|
|
168
|
+
const { key } = ammo.payload;
|
|
169
|
+
|
|
170
|
+
const value = await redis.get(key);
|
|
171
|
+
|
|
172
|
+
if (!value) {
|
|
173
|
+
return ammo.fire(404, { error: 'Key not found' });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
ammo.fire({ key, value });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
cache.register('/set', async (ammo) => {
|
|
180
|
+
if (!ammo.POST) return ammo.notAllowed();
|
|
181
|
+
|
|
182
|
+
const redis = dbManager.getConnection('redis');
|
|
183
|
+
const { key, value, ttl } = ammo.payload;
|
|
184
|
+
|
|
185
|
+
if (ttl) {
|
|
186
|
+
await redis.setEx(key, ttl, value);
|
|
187
|
+
} else {
|
|
188
|
+
await redis.set(key, value);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
ammo.fire(201, { message: 'Cached successfully' });
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### MongoDB Example
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
import { Target, TejError } from 'te.js';
|
|
199
|
+
import dbManager from 'te.js/database/index.js';
|
|
200
|
+
|
|
201
|
+
const users = new Target('/users');
|
|
202
|
+
|
|
203
|
+
users.register('/', async (ammo) => {
|
|
204
|
+
const mongo = dbManager.getConnection('mongodb');
|
|
205
|
+
const db = mongo.db('myapp');
|
|
206
|
+
const collection = db.collection('users');
|
|
207
|
+
|
|
208
|
+
if (ammo.GET) {
|
|
209
|
+
const users = await collection.find({}).toArray();
|
|
210
|
+
return ammo.fire(users);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (ammo.POST) {
|
|
214
|
+
const { name, email } = ammo.payload;
|
|
215
|
+
const result = await collection.insertOne({ name, email, createdAt: new Date() });
|
|
216
|
+
return ammo.fire(201, { id: result.insertedId, name, email });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
ammo.notAllowed();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
users.register('/:id', async (ammo) => {
|
|
223
|
+
const mongo = dbManager.getConnection('mongodb');
|
|
224
|
+
const db = mongo.db('myapp');
|
|
225
|
+
const collection = db.collection('users');
|
|
226
|
+
|
|
227
|
+
const { id } = ammo.payload;
|
|
228
|
+
const { ObjectId } = await import('mongodb');
|
|
229
|
+
|
|
230
|
+
const user = await collection.findOne({ _id: new ObjectId(id) });
|
|
231
|
+
|
|
232
|
+
if (!user) {
|
|
233
|
+
throw new TejError(404, 'User not found');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
ammo.fire(user);
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Database Manager API
|
|
241
|
+
|
|
242
|
+
Access the database manager directly for advanced usage:
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
import dbManager from 'te.js/database/index.js';
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Check Connection Status
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
const status = dbManager.hasConnection('redis', {});
|
|
252
|
+
// Returns: { exists: boolean, initializing: boolean }
|
|
253
|
+
|
|
254
|
+
if (status.exists) {
|
|
255
|
+
const redis = dbManager.getConnection('redis');
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The `initializing` flag is `true` while the connection is being established.
|
|
260
|
+
|
|
261
|
+
### Get All Active Connections
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
const connections = dbManager.getActiveConnections();
|
|
265
|
+
// Returns a Map: { 'redis' => { type, client, config }, 'mongodb' => { type, client, config } }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Close Connections
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
// Close a specific connection
|
|
272
|
+
await dbManager.closeConnection('redis');
|
|
273
|
+
|
|
274
|
+
// Close all connections
|
|
275
|
+
await dbManager.closeAllConnections();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Caching Pattern
|
|
279
|
+
|
|
280
|
+
A common pattern using Redis for caching:
|
|
281
|
+
|
|
282
|
+
```javascript
|
|
283
|
+
import { Target } from 'te.js';
|
|
284
|
+
import dbManager from 'te.js/database/index.js';
|
|
285
|
+
|
|
286
|
+
const api = new Target('/api');
|
|
287
|
+
|
|
288
|
+
// Cache middleware
|
|
289
|
+
const withCache = (ttl = 60) => async (ammo, next) => {
|
|
290
|
+
if (!ammo.GET) return next();
|
|
291
|
+
|
|
292
|
+
const redis = dbManager.getConnection('redis');
|
|
293
|
+
const cacheKey = `cache:${ammo.endpoint}`;
|
|
294
|
+
|
|
295
|
+
// Try cache first
|
|
296
|
+
const cached = await redis.get(cacheKey);
|
|
297
|
+
if (cached) {
|
|
298
|
+
ammo.res.setHeader('X-Cache', 'HIT');
|
|
299
|
+
return ammo.fire(JSON.parse(cached));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Store original fire to intercept
|
|
303
|
+
const originalFire = ammo.fire.bind(ammo);
|
|
304
|
+
ammo.fire = async (...args) => {
|
|
305
|
+
const data = args[0];
|
|
306
|
+
if (typeof data === 'object') {
|
|
307
|
+
await redis.setEx(cacheKey, ttl, JSON.stringify(data));
|
|
308
|
+
}
|
|
309
|
+
ammo.res.setHeader('X-Cache', 'MISS');
|
|
310
|
+
originalFire(...args);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
next();
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
api.register('/expensive-data', withCache(300), async (ammo) => {
|
|
317
|
+
// This expensive operation result will be cached for 5 minutes
|
|
318
|
+
const data = await expensiveOperation();
|
|
319
|
+
ammo.fire(data);
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Session Storage with Redis
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
327
|
+
import dbManager from 'te.js/database/index.js';
|
|
328
|
+
|
|
329
|
+
const sessionMiddleware = async (ammo, next) => {
|
|
330
|
+
const redis = dbManager.getConnection('redis');
|
|
331
|
+
let sessionId = ammo.headers['x-session-id'];
|
|
332
|
+
|
|
333
|
+
if (!sessionId) {
|
|
334
|
+
sessionId = uuidv4();
|
|
335
|
+
ammo.res.setHeader('X-Session-ID', sessionId);
|
|
336
|
+
ammo.session = {};
|
|
337
|
+
} else {
|
|
338
|
+
const sessionData = await redis.get(`session:${sessionId}`);
|
|
339
|
+
ammo.session = sessionData ? JSON.parse(sessionData) : {};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Save session after response
|
|
343
|
+
const originalFire = ammo.fire.bind(ammo);
|
|
344
|
+
ammo.fire = async (...args) => {
|
|
345
|
+
await redis.setEx(
|
|
346
|
+
`session:${sessionId}`,
|
|
347
|
+
3600, // 1 hour TTL
|
|
348
|
+
JSON.stringify(ammo.session)
|
|
349
|
+
);
|
|
350
|
+
originalFire(...args);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
next();
|
|
354
|
+
};
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Connection Events
|
|
358
|
+
|
|
359
|
+
The underlying clients emit events you can listen to:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
const redis = dbManager.getConnection('redis');
|
|
363
|
+
|
|
364
|
+
redis.on('error', (err) => {
|
|
365
|
+
console.error('Redis error:', err);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
redis.on('connect', () => {
|
|
369
|
+
console.log('Redis connected');
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
redis.on('reconnecting', () => {
|
|
373
|
+
console.log('Redis reconnecting...');
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Best Practices
|
|
378
|
+
|
|
379
|
+
1. **Initialize early** — Set up connections before `takeoff()`
|
|
380
|
+
2. **Handle errors** — Always wrap database operations in try/catch
|
|
381
|
+
3. **Use connection pooling** — MongoDB handles this automatically
|
|
382
|
+
4. **Close on shutdown** — Clean up connections when app terminates
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
// Graceful shutdown
|
|
386
|
+
process.on('SIGTERM', async () => {
|
|
387
|
+
await dbManager.closeAllConnections();
|
|
388
|
+
process.exit(0);
|
|
389
|
+
});
|
|
390
|
+
```
|