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.
Files changed (2) hide show
  1. package/README.md +33 -132
  2. 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
- ### Understanding `filter` vs `query`
266
+ ### Security & Filtering
277
267
 
278
- There are two ways data reaches your database:
268
+ **Two ways to query data:**
279
269
 
280
- 1. **`filter` (via `filterCustom`)** - For user-facing filters with transformations
281
- - Client-side `filters` prop → sends values to `filter` parameter
282
- - Server validates against `filterCustom` keys
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. **`query` (via `queryAllowed`)** - For direct field matching
287
- - Direct database field equality checks
288
- - Must explicitly whitelist with `queryAllowed`
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
- ### Security Note: Strict Mode by Default
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
- **`useDataKit`**: Error available in `state.error`
293
+ Errors are automatically displayed in `DataKitTable` or available via `state.error` in `useDataKit`.
338
294
 
339
295
  ```tsx
340
- const { state: { error } } = useDataKit({ action: fetchUsers });
341
-
342
- if (error) {
343
- return <div className="text-red-500">Error: {error.message}</div>;
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
- #### `escapeRegex(str)`
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 "next-data-kit/server";
305
+ import { createSearchFilter, escapeRegex } from 'next-data-kit/server';
367
306
 
368
- // ... inside dataKitServerAction options
369
307
  filterCustom: {
370
- // Manual search implementation
371
- search: (value) => {
372
- if (typeof value !== 'string') return {};
373
- const term = escapeRegex(value);
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
- #### Understanding `filterCustom` Flow
318
+ #### Filter Flow
390
319
 
391
- To use custom filters effectively, you must match the **Key** on the client with the **Key** on the server.
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
- const {
423
- actions: { setFilter },
424
- } = useDataKit({
425
- /* ... */
426
- });
323
+ // Client
324
+ <DataKitTable filters={[{ id: 'priceRange', label: 'Price', type: 'TEXT' }]} />
427
325
 
428
- // Trigger the manual search
429
- setFilter('search', 'query string');
326
+ // Server
327
+ filterCustom: {
328
+ priceRange: value => ({ price: { $lte: Number(value) } }),
329
+ }
430
330
 
431
- // Trigger the range filter
432
- setFilter('priceRange', { min: 10, max: 100 });
331
+ // Or use programmatically
332
+ const { actions: { setFilter } } = useDataKit({ ... });
333
+ setFilter('priceRange', 100);
433
334
  ```
434
335
 
435
336
  ### Client
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-data-kit",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "A powerful table utility for server-side pagination, filtering, and sorting with React components",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",