nttp 1.4.11 → 1.4.15
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 +20 -19
- package/dist/cli/docs.d.ts.map +1 -1
- package/dist/cli/docs.js +8 -0
- package/dist/cli/docs.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/executor.d.ts +9 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +118 -4
- package/dist/executor.js.map +1 -1
- package/docs/README.md +31 -0
- package/docs/api.md +571 -0
- package/docs/caching.md +579 -0
- package/docs/configuration.md +763 -0
- package/docs/examples.md +615 -0
- package/docs/models.md +423 -0
- package/docs/production.md +681 -0
- package/docs/troubleshooting.md +694 -0
- package/package.json +2 -1
package/docs/examples.md
ADDED
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
Comprehensive examples for using NTTP in various scenarios.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Basic Queries](#basic-queries)
|
|
8
|
+
- [Filtered Queries](#filtered-queries)
|
|
9
|
+
- [Aggregations](#aggregations)
|
|
10
|
+
- [Sorting and Limits](#sorting-and-limits)
|
|
11
|
+
- [Advanced Queries](#advanced-queries)
|
|
12
|
+
- [Error Handling](#error-handling)
|
|
13
|
+
- [Using with Express](#using-with-express)
|
|
14
|
+
- [Using with Next.js](#using-with-nextjs)
|
|
15
|
+
- [CLI Integration](#cli-integration)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Basic Queries
|
|
20
|
+
|
|
21
|
+
### Simple SELECT
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { NTTP } from 'nttp';
|
|
25
|
+
|
|
26
|
+
const nttp = await NTTP.fromEnv();
|
|
27
|
+
|
|
28
|
+
// Get all users
|
|
29
|
+
const users = await nttp.query("show me all users");
|
|
30
|
+
console.log(users.data);
|
|
31
|
+
// [{ id: 1, name: 'John', email: 'john@example.com' }, ...]
|
|
32
|
+
|
|
33
|
+
// Get all products
|
|
34
|
+
const products = await nttp.query("list all products");
|
|
35
|
+
|
|
36
|
+
// Get all orders
|
|
37
|
+
const orders = await nttp.query("show orders");
|
|
38
|
+
|
|
39
|
+
await nttp.close();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### Specific Fields
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// Get only specific fields
|
|
48
|
+
const emails = await nttp.query("show user emails");
|
|
49
|
+
// [{ email: 'john@example.com' }, { email: 'jane@example.com' }, ...]
|
|
50
|
+
|
|
51
|
+
// Multiple fields
|
|
52
|
+
const names = await nttp.query("show user names and emails");
|
|
53
|
+
// [{ name: 'John', email: 'john@example.com' }, ...]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Filtered Queries
|
|
59
|
+
|
|
60
|
+
### Simple Filters
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Filter by status
|
|
64
|
+
const active = await nttp.query("show active users");
|
|
65
|
+
// WHERE status = 'active'
|
|
66
|
+
|
|
67
|
+
// Filter by category
|
|
68
|
+
const electronics = await nttp.query("products in Electronics category");
|
|
69
|
+
// WHERE category = 'Electronics'
|
|
70
|
+
|
|
71
|
+
// Filter by state
|
|
72
|
+
const californiaUsers = await nttp.query("users from California");
|
|
73
|
+
// WHERE state = 'California'
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Multiple Filters
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Multiple conditions
|
|
82
|
+
const result = await nttp.query("active premium users from California");
|
|
83
|
+
// WHERE status = 'active' AND tier = 'premium' AND state = 'California'
|
|
84
|
+
|
|
85
|
+
// Complex filtering
|
|
86
|
+
const orders = await nttp.query("pending orders over $500");
|
|
87
|
+
// WHERE status = 'pending' AND total > 500
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Range Filters
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Price range
|
|
96
|
+
const affordable = await nttp.query("products under $50");
|
|
97
|
+
// WHERE price < 50
|
|
98
|
+
|
|
99
|
+
// Date range
|
|
100
|
+
const recent = await nttp.query("orders from the last 30 days");
|
|
101
|
+
// WHERE created_at > NOW() - INTERVAL '30 days'
|
|
102
|
+
|
|
103
|
+
// Rating filter
|
|
104
|
+
const topRated = await nttp.query("products with 4+ star rating");
|
|
105
|
+
// WHERE rating >= 4
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Aggregations
|
|
111
|
+
|
|
112
|
+
### COUNT
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// Count all
|
|
116
|
+
const userCount = await nttp.query("count all users");
|
|
117
|
+
console.log(userCount.data);
|
|
118
|
+
// [{ count: 1523 }]
|
|
119
|
+
|
|
120
|
+
// Count with filter
|
|
121
|
+
const pendingCount = await nttp.query("count pending orders");
|
|
122
|
+
// [{ count: 42 }]
|
|
123
|
+
|
|
124
|
+
// Count by group
|
|
125
|
+
const byStatus = await nttp.query("count users by status");
|
|
126
|
+
// [{ status: 'active', count: 1200 }, { status: 'inactive', count: 323 }]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### SUM / AVG
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// Total revenue
|
|
135
|
+
const revenue = await nttp.query("total revenue");
|
|
136
|
+
// [{ total: 125000.50 }]
|
|
137
|
+
|
|
138
|
+
// Average order value
|
|
139
|
+
const avgOrder = await nttp.query("average order value");
|
|
140
|
+
// [{ average: 85.25 }]
|
|
141
|
+
|
|
142
|
+
// Sum by category
|
|
143
|
+
const categoryRevenue = await nttp.query("total revenue by category");
|
|
144
|
+
// [{ category: 'Electronics', total: 50000 }, ...]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Sorting and Limits
|
|
150
|
+
|
|
151
|
+
### Sorting
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Sort ascending
|
|
155
|
+
const alphabetical = await nttp.query("users sorted alphabetically");
|
|
156
|
+
// ORDER BY name ASC
|
|
157
|
+
|
|
158
|
+
// Sort descending
|
|
159
|
+
const expensive = await nttp.query("products sorted by price highest first");
|
|
160
|
+
// ORDER BY price DESC
|
|
161
|
+
|
|
162
|
+
// Sort by date
|
|
163
|
+
const newest = await nttp.query("orders sorted by newest first");
|
|
164
|
+
// ORDER BY created_at DESC
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### Limits
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Fixed limit
|
|
173
|
+
const top5 = await nttp.query("show me 5 users");
|
|
174
|
+
// LIMIT 5
|
|
175
|
+
|
|
176
|
+
// Top N pattern
|
|
177
|
+
const top10Products = await nttp.query("top 10 products by price");
|
|
178
|
+
// ORDER BY price DESC LIMIT 10
|
|
179
|
+
|
|
180
|
+
// First N pattern
|
|
181
|
+
const recent20 = await nttp.query("first 20 recent orders");
|
|
182
|
+
// ORDER BY created_at DESC LIMIT 20
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Advanced Queries
|
|
188
|
+
|
|
189
|
+
### Checking Cache Performance
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
const result = await nttp.query("show active users");
|
|
193
|
+
|
|
194
|
+
if (result.meta) {
|
|
195
|
+
console.log(`Cache Layer: L${result.meta.cacheLayer}`);
|
|
196
|
+
console.log(`Cost: $${result.meta.cost}`);
|
|
197
|
+
console.log(`Latency: ${result.meta.latency}ms`);
|
|
198
|
+
|
|
199
|
+
if (result.meta.similarity) {
|
|
200
|
+
console.log(`Similarity: ${result.meta.similarity}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Output:
|
|
205
|
+
// Cache Layer: L2
|
|
206
|
+
// Cost: $0.0001
|
|
207
|
+
// Latency: 75ms
|
|
208
|
+
// Similarity: 0.92
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### Force Fresh Query
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// Skip cache, always generate new SQL
|
|
217
|
+
const fresh = await nttp.query("show users", {
|
|
218
|
+
useCache: false,
|
|
219
|
+
forceNewSchema: true
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
console.log(fresh.cacheHit); // false
|
|
223
|
+
console.log(fresh.sql); // Generated SQL
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### Explain Query
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// See what SQL would be generated without executing
|
|
232
|
+
const explanation = await nttp.explain("top 10 expensive products");
|
|
233
|
+
|
|
234
|
+
console.log('Intent:', explanation.intent);
|
|
235
|
+
// { entity: 'products', operation: 'list', sort: 'price:desc', limit: 10 }
|
|
236
|
+
|
|
237
|
+
console.log('SQL:', explanation.sql);
|
|
238
|
+
// SELECT * FROM products ORDER BY price DESC LIMIT ?
|
|
239
|
+
|
|
240
|
+
console.log('Params:', explanation.params);
|
|
241
|
+
// [10]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Error Handling
|
|
247
|
+
|
|
248
|
+
### Basic Try-Catch
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import {
|
|
252
|
+
IntentParseError,
|
|
253
|
+
SQLGenerationError,
|
|
254
|
+
SQLExecutionError
|
|
255
|
+
} from 'nttp';
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const result = await nttp.query("ambiguous query");
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error instanceof IntentParseError) {
|
|
261
|
+
console.error('Failed to understand query');
|
|
262
|
+
console.log('Suggestions:', error.suggestions);
|
|
263
|
+
} else if (error instanceof SQLGenerationError) {
|
|
264
|
+
console.error('Failed to generate SQL');
|
|
265
|
+
console.log('Suggestions:', error.suggestions);
|
|
266
|
+
} else if (error instanceof SQLExecutionError) {
|
|
267
|
+
console.error('Query execution failed');
|
|
268
|
+
console.log('SQL:', error.sql);
|
|
269
|
+
console.log('Suggestions:', error.suggestions);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### Production Error Handling
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
async function safeQuery(query: string) {
|
|
280
|
+
try {
|
|
281
|
+
return await nttp.query(query);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error instanceof IntentParseError) {
|
|
284
|
+
return {
|
|
285
|
+
error: 'Could not understand your query. Please try rephrasing.',
|
|
286
|
+
suggestions: error.suggestions
|
|
287
|
+
};
|
|
288
|
+
} else if (error instanceof SQLGenerationError) {
|
|
289
|
+
return {
|
|
290
|
+
error: 'Could not generate database query. Please simplify your request.',
|
|
291
|
+
suggestions: error.suggestions
|
|
292
|
+
};
|
|
293
|
+
} else if (error instanceof SQLExecutionError) {
|
|
294
|
+
return {
|
|
295
|
+
error: 'Database error occurred. Please contact support.',
|
|
296
|
+
suggestions: error.suggestions
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
throw error; // Re-throw unknown errors
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const result = await safeQuery("show users");
|
|
304
|
+
if ('error' in result) {
|
|
305
|
+
console.error(result.error);
|
|
306
|
+
} else {
|
|
307
|
+
console.log(result.data);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Using with Express
|
|
314
|
+
|
|
315
|
+
### Basic API Endpoint
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import express from 'express';
|
|
319
|
+
import { NTTP } from 'nttp';
|
|
320
|
+
|
|
321
|
+
const app = express();
|
|
322
|
+
const nttp = await NTTP.fromEnv();
|
|
323
|
+
|
|
324
|
+
app.get('/api/query', async (req, res) => {
|
|
325
|
+
try {
|
|
326
|
+
const { q } = req.query;
|
|
327
|
+
|
|
328
|
+
if (!q || typeof q !== 'string') {
|
|
329
|
+
return res.status(400).json({ error: 'Query parameter "q" required' });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const result = await nttp.query(q);
|
|
333
|
+
|
|
334
|
+
res.json({
|
|
335
|
+
data: result.data,
|
|
336
|
+
cacheHit: result.cacheHit,
|
|
337
|
+
meta: result.meta
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
res.status(500).json({
|
|
341
|
+
error: error.message,
|
|
342
|
+
suggestions: error.suggestions || []
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
app.listen(3000, () => console.log('Server running on port 3000'));
|
|
348
|
+
|
|
349
|
+
// Usage:
|
|
350
|
+
// GET /api/query?q=show%20active%20users
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
### With Rate Limiting
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import rateLimit from 'express-rate-limit';
|
|
359
|
+
|
|
360
|
+
const queryLimiter = rateLimit({
|
|
361
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
362
|
+
max: 100, // Limit each IP to 100 requests per windowMs
|
|
363
|
+
message: 'Too many requests, please try again later'
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
app.get('/api/query', queryLimiter, async (req, res) => {
|
|
367
|
+
// ... query logic
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Using with Next.js
|
|
374
|
+
|
|
375
|
+
### App Router (Server Actions)
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// app/actions.ts
|
|
379
|
+
'use server';
|
|
380
|
+
|
|
381
|
+
import { NTTP } from 'nttp';
|
|
382
|
+
|
|
383
|
+
let nttpInstance: NTTP | null = null;
|
|
384
|
+
|
|
385
|
+
async function getNTTP() {
|
|
386
|
+
if (!nttpInstance) {
|
|
387
|
+
nttpInstance = await NTTP.fromEnv();
|
|
388
|
+
}
|
|
389
|
+
return nttpInstance;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export async function queryDatabase(query: string) {
|
|
393
|
+
try {
|
|
394
|
+
const nttp = await getNTTP();
|
|
395
|
+
const result = await nttp.query(query);
|
|
396
|
+
|
|
397
|
+
return {
|
|
398
|
+
success: true,
|
|
399
|
+
data: result.data,
|
|
400
|
+
cacheHit: result.cacheHit,
|
|
401
|
+
meta: result.meta
|
|
402
|
+
};
|
|
403
|
+
} catch (error) {
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
error: error.message,
|
|
407
|
+
suggestions: error.suggestions || []
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// app/page.tsx
|
|
415
|
+
'use client';
|
|
416
|
+
|
|
417
|
+
import { useState } from 'react';
|
|
418
|
+
import { queryDatabase } from './actions';
|
|
419
|
+
|
|
420
|
+
export default function QueryPage() {
|
|
421
|
+
const [query, setQuery] = useState('');
|
|
422
|
+
const [results, setResults] = useState(null);
|
|
423
|
+
const [loading, setLoading] = useState(false);
|
|
424
|
+
|
|
425
|
+
async function handleQuery() {
|
|
426
|
+
setLoading(true);
|
|
427
|
+
const result = await queryDatabase(query);
|
|
428
|
+
setResults(result);
|
|
429
|
+
setLoading(false);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
<div>
|
|
434
|
+
<input
|
|
435
|
+
value={query}
|
|
436
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
437
|
+
placeholder="Ask a question..."
|
|
438
|
+
/>
|
|
439
|
+
<button onClick={handleQuery} disabled={loading}>
|
|
440
|
+
{loading ? 'Loading...' : 'Query'}
|
|
441
|
+
</button>
|
|
442
|
+
|
|
443
|
+
{results?.success && (
|
|
444
|
+
<pre>{JSON.stringify(results.data, null, 2)}</pre>
|
|
445
|
+
)}
|
|
446
|
+
|
|
447
|
+
{results?.error && (
|
|
448
|
+
<div className="error">
|
|
449
|
+
{results.error}
|
|
450
|
+
<ul>
|
|
451
|
+
{results.suggestions.map((s, i) => <li key={i}>{s}</li>)}
|
|
452
|
+
</ul>
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
</div>
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
### API Routes
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// app/api/query/route.ts
|
|
466
|
+
import { NTTP } from 'nttp';
|
|
467
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
468
|
+
|
|
469
|
+
let nttp: NTTP | null = null;
|
|
470
|
+
|
|
471
|
+
async function getNTTP() {
|
|
472
|
+
if (!nttp) {
|
|
473
|
+
nttp = await NTTP.fromEnv();
|
|
474
|
+
}
|
|
475
|
+
return nttp;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export async function GET(request: NextRequest) {
|
|
479
|
+
const searchParams = request.nextUrl.searchParams;
|
|
480
|
+
const query = searchParams.get('q');
|
|
481
|
+
|
|
482
|
+
if (!query) {
|
|
483
|
+
return NextResponse.json(
|
|
484
|
+
{ error: 'Query parameter required' },
|
|
485
|
+
{ status: 400 }
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
try {
|
|
490
|
+
const nttpInstance = await getNTTP();
|
|
491
|
+
const result = await nttpInstance.query(query);
|
|
492
|
+
|
|
493
|
+
return NextResponse.json({
|
|
494
|
+
data: result.data,
|
|
495
|
+
cacheHit: result.cacheHit,
|
|
496
|
+
meta: result.meta
|
|
497
|
+
});
|
|
498
|
+
} catch (error: any) {
|
|
499
|
+
return NextResponse.json(
|
|
500
|
+
{
|
|
501
|
+
error: error.message,
|
|
502
|
+
suggestions: error.suggestions || []
|
|
503
|
+
},
|
|
504
|
+
{ status: 500 }
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## CLI Integration
|
|
513
|
+
|
|
514
|
+
### Simple CLI Tool
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
#!/usr/bin/env node
|
|
518
|
+
import { NTTP } from 'nttp';
|
|
519
|
+
|
|
520
|
+
const query = process.argv.slice(2).join(' ');
|
|
521
|
+
|
|
522
|
+
if (!query) {
|
|
523
|
+
console.error('Usage: query-cli <natural language query>');
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const nttp = await NTTP.fromEnv();
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const result = await nttp.query(query);
|
|
531
|
+
|
|
532
|
+
console.log('\nResults:');
|
|
533
|
+
console.table(result.data);
|
|
534
|
+
|
|
535
|
+
if (result.meta) {
|
|
536
|
+
console.log(`\nCache: L${result.meta.cacheLayer} | Cost: $${result.meta.cost} | Latency: ${result.meta.latency}ms`);
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.error('Error:', error.message);
|
|
540
|
+
if (error.suggestions) {
|
|
541
|
+
console.log('\nSuggestions:');
|
|
542
|
+
error.suggestions.forEach((s: string) => console.log(` • ${s}`));
|
|
543
|
+
}
|
|
544
|
+
process.exit(1);
|
|
545
|
+
} finally {
|
|
546
|
+
await nttp.close();
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
**Usage:**
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
./query-cli "show active users"
|
|
554
|
+
./query-cli "count pending orders"
|
|
555
|
+
./query-cli "top 10 products by price"
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
|
|
560
|
+
### Interactive CLI
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
#!/usr/bin/env node
|
|
564
|
+
import { NTTP } from 'nttp';
|
|
565
|
+
import readline from 'readline';
|
|
566
|
+
|
|
567
|
+
const rl = readline.createInterface({
|
|
568
|
+
input: process.stdin,
|
|
569
|
+
output: process.stdout,
|
|
570
|
+
prompt: 'nttp> '
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
const nttp = await NTTP.fromEnv();
|
|
574
|
+
|
|
575
|
+
console.log('NTTP Interactive Query Tool');
|
|
576
|
+
console.log('Type your questions in natural language, or "exit" to quit\n');
|
|
577
|
+
|
|
578
|
+
rl.prompt();
|
|
579
|
+
|
|
580
|
+
rl.on('line', async (line) => {
|
|
581
|
+
const query = line.trim();
|
|
582
|
+
|
|
583
|
+
if (query === 'exit' || query === 'quit') {
|
|
584
|
+
await nttp.close();
|
|
585
|
+
process.exit(0);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (!query) {
|
|
589
|
+
rl.prompt();
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
const result = await nttp.query(query);
|
|
595
|
+
console.table(result.data);
|
|
596
|
+
|
|
597
|
+
if (result.meta) {
|
|
598
|
+
console.log(`L${result.meta.cacheLayer} | $${result.meta.cost} | ${result.meta.latency}ms`);
|
|
599
|
+
}
|
|
600
|
+
} catch (error) {
|
|
601
|
+
console.error('Error:', error.message);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
rl.prompt();
|
|
605
|
+
});
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## See Also
|
|
611
|
+
|
|
612
|
+
- [API Reference](./api.md) - Complete API documentation
|
|
613
|
+
- [Configuration](./configuration.md) - Configuration options
|
|
614
|
+
- [Production Guide](./production.md) - Production deployment tips
|
|
615
|
+
- [Troubleshooting](./troubleshooting.md) - Common issues
|