next-data-kit 3.0.0 → 3.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 +33 -132
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,18 +43,10 @@ export async function fetchUsers(input: TDataKitInput) {
|
|
|
43
43
|
name: user.name,
|
|
44
44
|
email: user.email,
|
|
45
45
|
}),
|
|
46
|
-
filter: () => ({
|
|
47
|
-
active: true,
|
|
48
|
-
}),
|
|
49
46
|
filterCustom: {
|
|
50
47
|
search: createSearchFilter(['name', 'email']),
|
|
51
|
-
age: value => ({
|
|
52
|
-
age: {
|
|
53
|
-
$gte: value,
|
|
54
|
-
},
|
|
55
|
-
}),
|
|
48
|
+
age: value => ({ age: { $gte: value } }),
|
|
56
49
|
},
|
|
57
|
-
// Only 'search' and 'age' filters are allowed (auto-extracted from filterCustom keys)
|
|
58
50
|
});
|
|
59
51
|
}
|
|
60
52
|
```
|
|
@@ -69,7 +61,6 @@ You can use the built-in Zod schema to validate inputs before processing:
|
|
|
69
61
|
import { dataKitServerAction, dataKitSchemaZod } from 'next-data-kit/server';
|
|
70
62
|
|
|
71
63
|
export async function fetchUsers(input: unknown) {
|
|
72
|
-
// Validate input
|
|
73
64
|
const parsedInput = dataKitSchemaZod.parse(input);
|
|
74
65
|
|
|
75
66
|
return dataKitServerAction({
|
|
@@ -80,7 +71,6 @@ export async function fetchUsers(input: unknown) {
|
|
|
80
71
|
search: value => ({ name: { $regex: value, $options: 'i' } }),
|
|
81
72
|
role: value => ({ role: value }),
|
|
82
73
|
},
|
|
83
|
-
// Only 'search' and 'role' filters are allowed
|
|
84
74
|
});
|
|
85
75
|
}
|
|
86
76
|
```
|
|
@@ -273,163 +263,74 @@ type TDataKitServerActionOptions<T, R> = {
|
|
|
273
263
|
}
|
|
274
264
|
```
|
|
275
265
|
|
|
276
|
-
###
|
|
266
|
+
### Security & Filtering
|
|
277
267
|
|
|
278
|
-
|
|
268
|
+
**Two ways to query data:**
|
|
279
269
|
|
|
280
|
-
1. **`
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
- You define how values transform into database queries
|
|
284
|
-
- **Use for**: search boxes, dropdowns, date ranges, etc.
|
|
270
|
+
1. **`filterCustom`** - User-facing filters (search, dropdowns, etc.)
|
|
271
|
+
- Client `filters` prop → validated against `filterCustom` keys
|
|
272
|
+
- Only defined keys are allowed (throws error otherwise)
|
|
285
273
|
|
|
286
|
-
2. **`
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
- **Use for**: fixed filters like `{ active: true }`, user-specific queries
|
|
274
|
+
2. **`queryAllowed`** - Direct field matching (fixed filters)
|
|
275
|
+
- Explicit whitelist required
|
|
276
|
+
- Use for: `{ active: true }`, user-specific queries
|
|
290
277
|
|
|
291
278
|
```typescript
|
|
292
279
|
dataKitServerAction({
|
|
293
280
|
model: UserModel,
|
|
294
281
|
input,
|
|
295
282
|
item: u => u,
|
|
296
|
-
// Client filters go through filterCustom
|
|
297
283
|
filterCustom: {
|
|
298
284
|
search: createSearchFilter(['name', 'email']),
|
|
299
285
|
role: value => ({ role: value }),
|
|
300
286
|
},
|
|
301
|
-
// Direct queries need explicit whitelist
|
|
302
287
|
queryAllowed: ['organizationId', 'active'],
|
|
303
288
|
});
|
|
304
289
|
```
|
|
305
290
|
|
|
306
|
-
###
|
|
307
|
-
|
|
308
|
-
**Filter Security**: When you define `filterCustom`, ONLY those keys are allowed. Any other filter key from the client will **THROW AN ERROR**.
|
|
309
|
-
|
|
310
|
-
**Query Security**: When you provide `queryAllowed`, only those query fields are accepted. Any other query field will throw an error.
|
|
311
|
-
|
|
312
|
-
````typescript
|
|
313
|
-
// Strict Security Example
|
|
314
|
-
dataKitServerAction({
|
|
315
|
-
model: UserModel,
|
|
316
|
-
input,
|
|
317
|
-
item: u => ({ id: u._id.toString(), name: u.name }),
|
|
318
|
-
filterCustom: {
|
|
319
|
-
name: value => ({ name: { $regex: value, $options: 'i' } }),
|
|
320
|
-
email: value => ({ email: { $regex: value, $options: 'i' } }),
|
|
321
|
-
role: value => ({ role: value }),
|
|
322
|
-
},
|
|
323
|
-
// ONLY 'name', 'email', and 'role' filters are allowed
|
|
324
|
-
// If client sends { filter: { secret: "true" } }, this WILL THROW an Error!
|
|
325
|
-
|
|
326
|
-
// Query params need explicit whitelist
|
|
327
|
-
queryAllowed: ['status'],
|
|
328
|
-
});
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### Error Handling on Client
|
|
332
|
-
|
|
333
|
-
When the server action throws an error (e.g., security violation), the client automatically handles it:
|
|
334
|
-
|
|
335
|
-
**`DataKitTable`**: Displays error in red within the table body
|
|
291
|
+
### Error Handling
|
|
336
292
|
|
|
337
|
-
|
|
293
|
+
Errors are automatically displayed in `DataKitTable` or available via `state.error` in `useDataKit`.
|
|
338
294
|
|
|
339
295
|
```tsx
|
|
340
|
-
const {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
#### `createSearchFilter(fields)`
|
|
348
|
-
|
|
349
|
-
Create a search filter for multiple fields. Use this in `filterCustom`.
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
filterCustom: {
|
|
353
|
-
search: createSearchFilter(['name', 'email', 'phone']),
|
|
354
|
-
}
|
|
296
|
+
const {
|
|
297
|
+
state: { error },
|
|
298
|
+
} = useDataKit({ action: fetchUsers });
|
|
299
|
+
if (error) return <div>Error: {error.message}</div>;
|
|
355
300
|
```
|
|
356
301
|
|
|
357
|
-
####
|
|
358
|
-
|
|
359
|
-
Escape regex special characters in a string.
|
|
360
|
-
|
|
361
|
-
#### Custom Filter Implementation
|
|
362
|
-
|
|
363
|
-
You can implement custom filters manually without using `createSearchFilter`. This gives you full control over the database query.
|
|
302
|
+
#### Custom Filters
|
|
364
303
|
|
|
365
304
|
```typescript
|
|
366
|
-
import { escapeRegex } from
|
|
305
|
+
import { createSearchFilter, escapeRegex } from 'next-data-kit/server';
|
|
367
306
|
|
|
368
|
-
// ... inside dataKitServerAction options
|
|
369
307
|
filterCustom: {
|
|
370
|
-
//
|
|
371
|
-
search: (
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
return {
|
|
375
|
-
$or: [
|
|
376
|
-
{ name: { $regex: term, $options: "i" } },
|
|
377
|
-
{ email: { $regex: term, $options: "i" } },
|
|
378
|
-
],
|
|
379
|
-
};
|
|
380
|
-
},
|
|
381
|
-
// Filter by range
|
|
308
|
+
// Use built-in helper
|
|
309
|
+
search: createSearchFilter(['name', 'email', 'phone']),
|
|
310
|
+
|
|
311
|
+
// Or implement custom logic
|
|
382
312
|
priceRange: (value: { min: number; max: number }) => ({
|
|
383
313
|
price: { $gte: value.min, $lte: value.max },
|
|
384
314
|
}),
|
|
385
|
-
}
|
|
386
|
-
// Only 'search' and 'priceRange' filters are allowed from client
|
|
315
|
+
}
|
|
387
316
|
```
|
|
388
317
|
|
|
389
|
-
####
|
|
318
|
+
#### Filter Flow
|
|
390
319
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
1. **Client-side**: Define a filter with a specific `id` (e.g., `'priceRange'`).
|
|
394
|
-
|
|
395
|
-
```tsx
|
|
396
|
-
// Client Component
|
|
397
|
-
<DataKitTable
|
|
398
|
-
filters={[{ id: 'priceRange', label: 'Price Range', type: 'TEXT' }]}
|
|
399
|
-
// ...
|
|
400
|
-
/>
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
_Note: When use interact with this filter, `DataKit` sends `{ filter: { priceRange: "value" } }` to the server._
|
|
404
|
-
|
|
405
|
-
2. **Server-side**: Handle that key in `filterCustom`.
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
// Server Action
|
|
409
|
-
filterCustom: {
|
|
410
|
-
// MATCHES 'priceRange' FROM CLIENT
|
|
411
|
-
priceRange: value => ({
|
|
412
|
-
price: { $lte: Number(value) },
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
The `filterCustom` function intercepts the value sent from the client before it hits the database query builder, allowing you to transform simple values into complex queries.
|
|
418
|
-
|
|
419
|
-
**Client Usage:**
|
|
320
|
+
Match client filter `id` with server `filterCustom` key:
|
|
420
321
|
|
|
421
322
|
```tsx
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
} = useDataKit({
|
|
425
|
-
/* ... */
|
|
426
|
-
});
|
|
323
|
+
// Client
|
|
324
|
+
<DataKitTable filters={[{ id: 'priceRange', label: 'Price', type: 'TEXT' }]} />
|
|
427
325
|
|
|
428
|
-
//
|
|
429
|
-
|
|
326
|
+
// Server
|
|
327
|
+
filterCustom: {
|
|
328
|
+
priceRange: value => ({ price: { $lte: Number(value) } }),
|
|
329
|
+
}
|
|
430
330
|
|
|
431
|
-
//
|
|
432
|
-
|
|
331
|
+
// Or use programmatically
|
|
332
|
+
const { actions: { setFilter } } = useDataKit({ ... });
|
|
333
|
+
setFilter('priceRange', 100);
|
|
433
334
|
```
|
|
434
335
|
|
|
435
336
|
### Client
|
package/package.json
CHANGED