@promakeai/dbreact 1.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.
- package/README.md +633 -0
- package/SKILL.md +311 -0
- package/dist/adapters/RestAdapter.d.ts.map +1 -0
- package/dist/adapters/SqliteAdapter.d.ts.map +1 -0
- package/dist/adapters/SqliteAdapter.js +543 -0
- package/dist/core/DataManager.d.ts.map +1 -0
- package/dist/hooks/useDataManager.d.ts.map +1 -0
- package/dist/hooks/useDbHooks.d.ts.map +1 -0
- package/dist/hooks/useDbHooks.js +157 -0
- package/dist/hooks/useDbLang.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +648 -0
- package/dist/providers/DbProvider.d.ts.map +1 -0
- package/dist/providers/DbProvider.js +134 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/utils/whereBuilder.d.ts.map +1 -0
- package/package.json +54 -0
- package/providers/DbProvider.tsx +191 -0
package/README.md
ADDED
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
# @promakeai/dbreact
|
|
2
|
+
|
|
3
|
+
React hooks and providers for schema-driven, multi-language databases. Type-safe, with automatic translation support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-Safe Hooks** - Generated TypeScript types and React hooks
|
|
8
|
+
- **Multi-Language Support** - Automatic translation queries with fallback
|
|
9
|
+
- **Browser SQLite** - sql.js WASM adapter for offline-first apps
|
|
10
|
+
- **React Query Integration** - Built-in caching, loading states, optimistic updates
|
|
11
|
+
- **Zero-Config Language Switching** - Change language, queries refetch automatically
|
|
12
|
+
- **MongoDB-Style Queries** - Intuitive filter syntax (`$gt`, `$in`, `$like`, etc.)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @promakeai/dbreact @tanstack/react-query
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Peer Dependencies:**
|
|
21
|
+
- `react` >= 18.0.0
|
|
22
|
+
- `@tanstack/react-query` >= 5.0.0
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Setup Adapter
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { SqliteAdapter } from '@promakeai/dbreact';
|
|
30
|
+
|
|
31
|
+
const adapter = new SqliteAdapter({
|
|
32
|
+
storageKey: 'myapp-db', // localStorage key for persistence
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Wrap App with Provider
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { DbProvider } from '@promakeai/dbreact';
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
const [lang, setLang] = useState('en');
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<DbProvider
|
|
46
|
+
adapter={adapter}
|
|
47
|
+
lang={lang}
|
|
48
|
+
fallbackLang="en"
|
|
49
|
+
autoConnect={true}
|
|
50
|
+
>
|
|
51
|
+
<MyApp />
|
|
52
|
+
</DbProvider>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Use Hooks
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useDbList, useDbGet, useDbCreate } from '@promakeai/dbreact';
|
|
61
|
+
|
|
62
|
+
function ProductList() {
|
|
63
|
+
// List with filters
|
|
64
|
+
const { data: products, isLoading } = useDbList('products', {
|
|
65
|
+
where: { stock: { $gt: 0 } },
|
|
66
|
+
orderBy: [{ field: 'name', direction: 'ASC' }],
|
|
67
|
+
limit: 20,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Create mutation
|
|
71
|
+
const createProduct = useDbCreate('products');
|
|
72
|
+
|
|
73
|
+
const handleCreate = () => {
|
|
74
|
+
createProduct.mutate({
|
|
75
|
+
sku: 'NEW-001',
|
|
76
|
+
price: 99.99,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
if (isLoading) return <div>Loading...</div>;
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div>
|
|
84
|
+
{products?.map(p => <ProductCard key={p.id} product={p} />)}
|
|
85
|
+
<button onClick={handleCreate}>Add Product</button>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## API Reference
|
|
94
|
+
|
|
95
|
+
### DbProvider
|
|
96
|
+
|
|
97
|
+
Main provider component that wraps your application.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<DbProvider
|
|
101
|
+
adapter={adapter}
|
|
102
|
+
lang="tr"
|
|
103
|
+
fallbackLang="en"
|
|
104
|
+
autoConnect={true}
|
|
105
|
+
>
|
|
106
|
+
<App />
|
|
107
|
+
</DbProvider>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Props:**
|
|
111
|
+
|
|
112
|
+
| Prop | Type | Required | Default | Description |
|
|
113
|
+
|------|------|----------|---------|-------------|
|
|
114
|
+
| `adapter` | `IDataAdapter` | Yes | - | Database adapter instance |
|
|
115
|
+
| `lang` | `string` | No | `'en'` | Current language code |
|
|
116
|
+
| `fallbackLang` | `string` | No | `'en'` | Fallback language |
|
|
117
|
+
| `autoConnect` | `boolean` | No | `true` | Auto-connect on mount |
|
|
118
|
+
| `children` | `ReactNode` | Yes | - | Child components |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### Query Hooks
|
|
123
|
+
|
|
124
|
+
#### useDbList
|
|
125
|
+
|
|
126
|
+
Fetch multiple records with filtering, pagination, and sorting.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
const { data, isLoading, error, refetch } = useDbList<Product>('products', {
|
|
130
|
+
where: { stock: { $gt: 0 } },
|
|
131
|
+
orderBy: [{ field: 'price', direction: 'DESC' }],
|
|
132
|
+
limit: 20,
|
|
133
|
+
offset: 0,
|
|
134
|
+
enabled: true,
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Options:**
|
|
139
|
+
|
|
140
|
+
| Option | Type | Description |
|
|
141
|
+
|--------|------|-------------|
|
|
142
|
+
| `where` | `object` | MongoDB-style filter conditions |
|
|
143
|
+
| `orderBy` | `array` | Sort order `[{ field, direction }]` |
|
|
144
|
+
| `limit` | `number` | Max records to return |
|
|
145
|
+
| `offset` | `number` | Skip records |
|
|
146
|
+
| `populate` | `string[]` | Resolve foreign key references |
|
|
147
|
+
| `enabled` | `boolean` | Enable/disable query |
|
|
148
|
+
|
|
149
|
+
#### useDbGet
|
|
150
|
+
|
|
151
|
+
Fetch single record by ID.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
const { data: product, isLoading } = useDbGet<Product>('products', productId, {
|
|
155
|
+
populate: ['categoryId'],
|
|
156
|
+
enabled: !!productId,
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### Mutation Hooks
|
|
163
|
+
|
|
164
|
+
#### useDbCreate
|
|
165
|
+
|
|
166
|
+
Create a new record.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
const createProduct = useDbCreate<Product>('products');
|
|
170
|
+
|
|
171
|
+
createProduct.mutate(
|
|
172
|
+
{ sku: 'NEW-001', price: 99.99 },
|
|
173
|
+
{
|
|
174
|
+
onSuccess: (data) => console.log('Created:', data),
|
|
175
|
+
onError: (error) => console.error(error),
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### useDbUpdate
|
|
181
|
+
|
|
182
|
+
Update an existing record.
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
const updateProduct = useDbUpdate<Product>('products', productId);
|
|
186
|
+
|
|
187
|
+
updateProduct.mutate(
|
|
188
|
+
{ price: 89.99 },
|
|
189
|
+
{
|
|
190
|
+
onSuccess: () => console.log('Updated'),
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### useDbDelete
|
|
196
|
+
|
|
197
|
+
Delete a record.
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const deleteProduct = useDbDelete('products', productId);
|
|
201
|
+
|
|
202
|
+
deleteProduct.mutate(undefined, {
|
|
203
|
+
onSuccess: () => console.log('Deleted'),
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### Context Hooks
|
|
210
|
+
|
|
211
|
+
#### useDb
|
|
212
|
+
|
|
213
|
+
Access database connection status.
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
const { adapter, isConnected, error } = useDb();
|
|
217
|
+
|
|
218
|
+
if (!isConnected) return <div>Connecting...</div>;
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### useAdapter
|
|
222
|
+
|
|
223
|
+
Access adapter directly for raw queries.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
const adapter = useAdapter();
|
|
227
|
+
|
|
228
|
+
const handleRawQuery = async () => {
|
|
229
|
+
const results = await adapter.raw('SELECT * FROM products WHERE price > ?', [100]);
|
|
230
|
+
console.log(results);
|
|
231
|
+
};
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
#### useDbLang
|
|
235
|
+
|
|
236
|
+
Access and control language settings.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
const { lang, fallbackLang, setLang } = useDbLang();
|
|
240
|
+
|
|
241
|
+
// Language switcher
|
|
242
|
+
<select value={lang} onChange={e => setLang(e.target.value)}>
|
|
243
|
+
<option value="en">English</option>
|
|
244
|
+
<option value="tr">Turkce</option>
|
|
245
|
+
<option value="de">Deutsch</option>
|
|
246
|
+
</select>
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### SqliteAdapter
|
|
252
|
+
|
|
253
|
+
Browser-based SQLite storage using sql.js (WebAssembly).
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
const adapter = new SqliteAdapter({
|
|
257
|
+
storageKey: 'myapp-db', // localStorage key for persistence
|
|
258
|
+
schema: mySchema, // Optional: schema definition
|
|
259
|
+
initialData: seedData, // Optional: initial seed data
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Features:**
|
|
264
|
+
- Automatic persistence to localStorage
|
|
265
|
+
- Full SQL support
|
|
266
|
+
- Works offline
|
|
267
|
+
- WASM-based (no native dependencies)
|
|
268
|
+
|
|
269
|
+
**Limitations:**
|
|
270
|
+
- Data stored in browser only
|
|
271
|
+
- localStorage limit: ~5-10MB (browser dependent)
|
|
272
|
+
- Best for < 10,000 records
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Query Options
|
|
277
|
+
|
|
278
|
+
### MongoDB-Style Filters
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// Comparison
|
|
282
|
+
{ price: { $gt: 100 } } // > 100
|
|
283
|
+
{ price: { $gte: 100 } } // >= 100
|
|
284
|
+
{ price: { $lt: 100 } } // < 100
|
|
285
|
+
{ price: { $lte: 100 } } // <= 100
|
|
286
|
+
{ price: { $ne: 100 } } // != 100
|
|
287
|
+
{ price: { $eq: 100 } } // = 100
|
|
288
|
+
|
|
289
|
+
// Array
|
|
290
|
+
{ id: { $in: [1, 2, 3] } } // IN (1, 2, 3)
|
|
291
|
+
{ id: { $nin: [1, 2, 3] } } // NOT IN (1, 2, 3)
|
|
292
|
+
|
|
293
|
+
// String
|
|
294
|
+
{ name: { $like: '%shirt%' } } // LIKE '%shirt%'
|
|
295
|
+
{ name: { $notLike: '%test%' } } // NOT LIKE '%test%'
|
|
296
|
+
|
|
297
|
+
// Range
|
|
298
|
+
{ price: { $between: [10, 100] } } // BETWEEN 10 AND 100
|
|
299
|
+
|
|
300
|
+
// Null
|
|
301
|
+
{ description: { $isNull: true } } // IS NULL
|
|
302
|
+
{ description: { $isNull: false } } // IS NOT NULL
|
|
303
|
+
|
|
304
|
+
// Logical
|
|
305
|
+
{ $and: [
|
|
306
|
+
{ price: { $gt: 50 } },
|
|
307
|
+
{ stock: { $gt: 0 } }
|
|
308
|
+
]}
|
|
309
|
+
|
|
310
|
+
{ $or: [
|
|
311
|
+
{ category: 'shirts' },
|
|
312
|
+
{ category: 'pants' }
|
|
313
|
+
]}
|
|
314
|
+
|
|
315
|
+
{ $not: { price: { $lt: 100 } } } // NOT (price < 100)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Query Interface
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
interface QueryOptions {
|
|
322
|
+
where?: Record<string, unknown>;
|
|
323
|
+
orderBy?: Array<{
|
|
324
|
+
field: string;
|
|
325
|
+
direction: 'ASC' | 'DESC';
|
|
326
|
+
}>;
|
|
327
|
+
limit?: number;
|
|
328
|
+
offset?: number;
|
|
329
|
+
populate?: string[];
|
|
330
|
+
lang?: string;
|
|
331
|
+
fallbackLang?: string;
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Multi-Language Support
|
|
338
|
+
|
|
339
|
+
### Schema with Translatable Fields
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { defineSchema, f } from '@promakeai/orm';
|
|
343
|
+
|
|
344
|
+
const schema = defineSchema({
|
|
345
|
+
languages: ['en', 'tr', 'de'],
|
|
346
|
+
tables: {
|
|
347
|
+
products: {
|
|
348
|
+
id: f.id(),
|
|
349
|
+
price: f.decimal(),
|
|
350
|
+
name: f.string().translatable(), // In translation table
|
|
351
|
+
description: f.text().translatable(), // In translation table
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Automatic Translation
|
|
358
|
+
|
|
359
|
+
Queries automatically use the current language from DbProvider:
|
|
360
|
+
|
|
361
|
+
```tsx
|
|
362
|
+
function ProductList() {
|
|
363
|
+
// Automatically fetches in current language
|
|
364
|
+
const { data: products } = useDbList('products');
|
|
365
|
+
|
|
366
|
+
// products[0].name is in Turkish if lang='tr'
|
|
367
|
+
// Falls back to English if Turkish translation missing
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Language Switching
|
|
372
|
+
|
|
373
|
+
When you change language, React Query automatically refetches all queries:
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
function LanguageSwitcher() {
|
|
377
|
+
const { lang, setLang } = useDbLang();
|
|
378
|
+
|
|
379
|
+
return (
|
|
380
|
+
<select value={lang} onChange={e => setLang(e.target.value)}>
|
|
381
|
+
<option value="en">English</option>
|
|
382
|
+
<option value="tr">Turkce</option>
|
|
383
|
+
</select>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Generated Hooks
|
|
391
|
+
|
|
392
|
+
Generate type-safe hooks from your schema:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
dbcli generate --schema ./schema.ts --output ./src/db/generated
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Generated files:**
|
|
399
|
+
- `types.ts` - TypeScript interfaces for each table
|
|
400
|
+
- `hooks.ts` - Type-safe hooks for each table
|
|
401
|
+
|
|
402
|
+
```tsx
|
|
403
|
+
// Generated hooks usage
|
|
404
|
+
import { useProducts, useProduct, useCreateProduct } from './db/generated/hooks';
|
|
405
|
+
|
|
406
|
+
function ProductManager() {
|
|
407
|
+
const { data: products } = useProducts({
|
|
408
|
+
where: { stock: { $gt: 0 } },
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const { data: product } = useProduct(1);
|
|
412
|
+
|
|
413
|
+
const createProduct = useCreateProduct();
|
|
414
|
+
|
|
415
|
+
return (
|
|
416
|
+
// ...
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Advanced Usage
|
|
424
|
+
|
|
425
|
+
### Custom React Query Options
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
const { data } = useDbList('products', {
|
|
429
|
+
where: { active: true },
|
|
430
|
+
}, {
|
|
431
|
+
// React Query options
|
|
432
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
433
|
+
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
434
|
+
refetchOnWindowFocus: false,
|
|
435
|
+
retry: 3,
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Direct Adapter Access
|
|
440
|
+
|
|
441
|
+
For complex queries not covered by hooks:
|
|
442
|
+
|
|
443
|
+
```tsx
|
|
444
|
+
function AdvancedSearch() {
|
|
445
|
+
const adapter = useAdapter();
|
|
446
|
+
const [results, setResults] = useState([]);
|
|
447
|
+
|
|
448
|
+
const handleSearch = async (query: string) => {
|
|
449
|
+
const products = await adapter.list('products', {
|
|
450
|
+
where: {
|
|
451
|
+
$or: [
|
|
452
|
+
{ name: { $like: `%${query}%` } },
|
|
453
|
+
{ description: { $like: `%${query}%` } },
|
|
454
|
+
],
|
|
455
|
+
},
|
|
456
|
+
limit: 50,
|
|
457
|
+
});
|
|
458
|
+
setResults(products);
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
return <SearchInput onSearch={handleSearch} />;
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Optimistic Updates
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
function ProductPrice({ product }) {
|
|
469
|
+
const queryClient = useQueryClient();
|
|
470
|
+
const updateProduct = useDbUpdate('products', product.id);
|
|
471
|
+
|
|
472
|
+
const handlePriceChange = async (newPrice: number) => {
|
|
473
|
+
// Optimistic update
|
|
474
|
+
queryClient.setQueryData(
|
|
475
|
+
['products', 'detail', product.id],
|
|
476
|
+
{ ...product, price: newPrice }
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
await updateProduct.mutateAsync({ price: newPrice });
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
return <PriceInput value={product.price} onChange={handlePriceChange} />;
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Examples
|
|
489
|
+
|
|
490
|
+
### E-Commerce Product List
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
function Shop() {
|
|
494
|
+
const { lang } = useDbLang();
|
|
495
|
+
const [category, setCategory] = useState(null);
|
|
496
|
+
|
|
497
|
+
const { data: products, isLoading } = useDbList('products', {
|
|
498
|
+
where: category ? { categoryId: category } : {},
|
|
499
|
+
orderBy: [{ field: 'name', direction: 'ASC' }],
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (isLoading) return <Spinner />;
|
|
503
|
+
|
|
504
|
+
return (
|
|
505
|
+
<div>
|
|
506
|
+
<CategoryFilter onSelect={setCategory} />
|
|
507
|
+
<ProductGrid products={products} />
|
|
508
|
+
</div>
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Multi-Language Blog
|
|
514
|
+
|
|
515
|
+
```tsx
|
|
516
|
+
function BlogPost({ slug }: { slug: string }) {
|
|
517
|
+
const { data: posts } = useDbList('posts', {
|
|
518
|
+
where: { slug, published: true },
|
|
519
|
+
limit: 1,
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const post = posts?.[0];
|
|
523
|
+
if (!post) return <NotFound />;
|
|
524
|
+
|
|
525
|
+
return (
|
|
526
|
+
<article>
|
|
527
|
+
<h1>{post.title}</h1> {/* Auto-translated */}
|
|
528
|
+
<div>{post.content}</div> {/* Auto-translated */}
|
|
529
|
+
</article>
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Real-Time Search
|
|
535
|
+
|
|
536
|
+
```tsx
|
|
537
|
+
function ProductSearch() {
|
|
538
|
+
const adapter = useAdapter();
|
|
539
|
+
const [query, setQuery] = useState('');
|
|
540
|
+
|
|
541
|
+
const { data: results } = useQuery({
|
|
542
|
+
queryKey: ['search', query],
|
|
543
|
+
queryFn: () => adapter.list('products', {
|
|
544
|
+
where: {
|
|
545
|
+
$or: [
|
|
546
|
+
{ name: { $like: `%${query}%` } },
|
|
547
|
+
{ sku: { $like: `%${query}%` } },
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
|
+
limit: 10,
|
|
551
|
+
}),
|
|
552
|
+
enabled: query.length > 2,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
return (
|
|
556
|
+
<div>
|
|
557
|
+
<input value={query} onChange={e => setQuery(e.target.value)} />
|
|
558
|
+
<SearchResults results={results} />
|
|
559
|
+
</div>
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## TypeScript Support
|
|
567
|
+
|
|
568
|
+
Full TypeScript support with generated types:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
import type { Product, ProductInput } from './db/generated/types';
|
|
572
|
+
|
|
573
|
+
// Type-safe queries
|
|
574
|
+
const products: Product[] = await adapter.list('products');
|
|
575
|
+
|
|
576
|
+
// Type-safe creates
|
|
577
|
+
const newProduct: ProductInput = {
|
|
578
|
+
sku: 'SHIRT-001',
|
|
579
|
+
price: 99.99,
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
await adapter.create('products', newProduct);
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Performance Tips
|
|
588
|
+
|
|
589
|
+
1. **Use pagination** for large datasets:
|
|
590
|
+
```tsx
|
|
591
|
+
const { data } = useDbList('products', { limit: 20, offset: page * 20 });
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
2. **Enable query conditionally**:
|
|
595
|
+
```tsx
|
|
596
|
+
const { data } = useDbGet('products', id, { enabled: !!id });
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
3. **Prefetch next page**:
|
|
600
|
+
```tsx
|
|
601
|
+
queryClient.prefetchQuery({
|
|
602
|
+
queryKey: ['products', 'list', { offset: (page + 1) * 20 }],
|
|
603
|
+
queryFn: () => adapter.list('products', { offset: (page + 1) * 20 }),
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Troubleshooting
|
|
610
|
+
|
|
611
|
+
**"useDbLang must be used within a DbProvider"**
|
|
612
|
+
- Ensure component is wrapped in DbProvider
|
|
613
|
+
|
|
614
|
+
**Queries return empty results**
|
|
615
|
+
- Check adapter is connected: `const { isConnected } = useDb()`
|
|
616
|
+
- Verify data exists in database
|
|
617
|
+
- Check query filters are correct
|
|
618
|
+
|
|
619
|
+
**Translations not working**
|
|
620
|
+
- Ensure schema has `.translatable()` fields
|
|
621
|
+
- Run `dbcli generate` to create translation tables
|
|
622
|
+
- Check translation records exist in `{table}_translations`
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Related Packages
|
|
627
|
+
|
|
628
|
+
- [@promakeai/orm](../orm) - Core ORM with schema DSL
|
|
629
|
+
- [@promakeai/dbcli](../dbcli) - CLI tool for database operations
|
|
630
|
+
|
|
631
|
+
## License
|
|
632
|
+
|
|
633
|
+
MIT
|