dzql 0.1.0-alpha.3 → 0.1.0
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/CLAUDE.md +931 -0
- package/GETTING_STARTED.md +2 -0
- package/README.md +19 -483
- package/REFERENCE.md +891 -0
- package/package.json +4 -4
- package/src/client/ws.js +186 -0
- package/src/server/db.js +73 -0
- package/src/server/index.js +71 -0
package/GETTING_STARTED.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
DZQL is a PostgreSQL framework that gives you **atomic real-time updates** via WebSocket. Every database change broadcasts instantly to all connected clients. Zero boilerplate.
|
|
4
4
|
|
|
5
|
+
> **See also:** [REFERENCE.md](REFERENCE.md) for complete API documentation | [CLAUDE.md](../../CLAUDE.md) for AI development guide
|
|
6
|
+
|
|
5
7
|
## The Core Pattern
|
|
6
8
|
|
|
7
9
|
1. **Schema = API**: Define a table → DZQL auto-creates CRUD endpoints
|
package/README.md
CHANGED
|
@@ -1,91 +1,27 @@
|
|
|
1
|
-
# DZQL
|
|
1
|
+
# DZQL
|
|
2
2
|
|
|
3
|
-
PostgreSQL-powered framework with automatic CRUD operations
|
|
3
|
+
PostgreSQL-powered framework with automatic CRUD operations and real-time WebSocket synchronization.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
npm install dzql
|
|
7
|
-
# or with Bun (no Node.js required)
|
|
8
|
-
bun add dzql
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Why DZQL?
|
|
12
|
-
|
|
13
|
-
### Before DZQL
|
|
14
|
-
```javascript
|
|
15
|
-
// Traditional approach: Write everything
|
|
16
|
-
app.post('/api/users', authenticate, validate, async (req, res) => {
|
|
17
|
-
try {
|
|
18
|
-
const user = await db.query('INSERT INTO users (...) VALUES (...)', [...]);
|
|
19
|
-
res.json(user);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
res.status(500).json({ error: error.message });
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
// Repeat for GET, PUT, DELETE, SEARCH, LOOKUP... = 50+ lines of boilerplate
|
|
25
|
-
```
|
|
5
|
+
## Documentation
|
|
26
6
|
|
|
27
|
-
|
|
28
|
-
```javascript
|
|
29
|
-
// That's it. All 5 operations work automatically.
|
|
30
|
-
// GET, SAVE, DELETE, LOOKUP, SEARCH
|
|
31
|
-
const user = await ws.api.save.users({ name: 'John' });
|
|
32
|
-
const results = await ws.api.search.users({ filters: {name: 'john'} });
|
|
33
|
-
```
|
|
7
|
+
All documentation is maintained in the repository root:
|
|
34
8
|
|
|
35
|
-
|
|
9
|
+
- **[README.md](../../README.md)** - Project overview and quick start
|
|
10
|
+
- **[GETTING_STARTED.md](GETTING_STARTED.md)** - Complete tutorial with working todo app
|
|
11
|
+
- **[REFERENCE.md](REFERENCE.md)** - Complete API reference
|
|
12
|
+
- **[CLAUDE.md](../../CLAUDE.md)** - Development guide for AI assistants
|
|
13
|
+
- **[Venues Example](../venues/)** - Full working application
|
|
36
14
|
|
|
37
|
-
|
|
38
|
-
✅ **Real-time WebSocket** - Automatic change notifications to all clients
|
|
39
|
-
✅ **PostgreSQL-native** - Leverage full SQL power when needed
|
|
40
|
-
✅ **Graph Rules** - Cascading operations without joins
|
|
41
|
-
✅ **Permissions & RLS** - Row-level security built-in
|
|
42
|
-
✅ **Full-text Search** - Built-in search with filters & pagination
|
|
43
|
-
✅ **Type-safe** - Uses PostgreSQL as source of truth
|
|
44
|
-
✅ **Framework-agnostic** - Works with any frontend (React, Vue, Svelte, plain JS)
|
|
45
|
-
✅ **Bun Native** - No Node.js required
|
|
15
|
+
## Quick Install
|
|
46
16
|
|
|
47
|
-
## Quick Start
|
|
48
|
-
|
|
49
|
-
### 1. Install
|
|
50
17
|
```bash
|
|
51
18
|
bun add dzql
|
|
19
|
+
# or
|
|
20
|
+
npm install dzql
|
|
52
21
|
```
|
|
53
22
|
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
docker run -d \
|
|
57
|
-
-e POSTGRES_PASSWORD=dzql \
|
|
58
|
-
-e POSTGRES_DB=dzql \
|
|
59
|
-
-p 5432:5432 \
|
|
60
|
-
postgres:latest
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
### 3. Create Server
|
|
64
|
-
```javascript
|
|
65
|
-
import { createServer } from 'dzql';
|
|
66
|
-
|
|
67
|
-
const server = createServer({ port: 3000 });
|
|
68
|
-
console.log('🚀 Server on ws://localhost:3000/ws');
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### 4. Initialize Database
|
|
72
|
-
```bash
|
|
73
|
-
# Apply DZQL core migrations (included in package)
|
|
74
|
-
psql -h localhost -U postgres -d dzql < node_modules/dzql/src/database/migrations/*.sql
|
|
75
|
-
|
|
76
|
-
# Register your entities
|
|
77
|
-
psql -h localhost -U postgres -d dzql << EOF
|
|
78
|
-
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, email TEXT);
|
|
79
|
-
|
|
80
|
-
SELECT dzql.register_entity(
|
|
81
|
-
'users',
|
|
82
|
-
'name',
|
|
83
|
-
array['name', 'email']
|
|
84
|
-
);
|
|
85
|
-
EOF
|
|
86
|
-
```
|
|
23
|
+
## Quick Example
|
|
87
24
|
|
|
88
|
-
### 5. Use from Client
|
|
89
25
|
```javascript
|
|
90
26
|
import { WebSocketManager } from 'dzql/client';
|
|
91
27
|
|
|
@@ -93,416 +29,16 @@ const ws = new WebSocketManager();
|
|
|
93
29
|
await ws.connect();
|
|
94
30
|
|
|
95
31
|
// All 5 operations work automatically
|
|
96
|
-
const user = await ws.api.save.users({ name: 'Alice'
|
|
97
|
-
const results = await ws.api.search.users({ filters: { name:
|
|
98
|
-
const deleted = await ws.api.delete.users({ id: user.id });
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## The 5 Operations
|
|
102
|
-
|
|
103
|
-
Every registered entity automatically gets these 5 operations:
|
|
104
|
-
|
|
105
|
-
### GET - Retrieve Single Record
|
|
106
|
-
```javascript
|
|
107
|
-
const user = await ws.api.get.users({ id: 1 });
|
|
108
|
-
// Server: await db.api.get.users({ id: 1 }, userId);
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### SAVE - Create or Update
|
|
112
|
-
```javascript
|
|
113
|
-
const user = await ws.api.save.users({
|
|
114
|
-
id: 1, // Optional - omit for insert
|
|
115
|
-
name: 'Alice',
|
|
116
|
-
email: 'alice@example.com'
|
|
117
|
-
});
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### DELETE - Remove Record
|
|
121
|
-
```javascript
|
|
122
|
-
const deleted = await ws.api.delete.users({ id: 1 });
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### LOOKUP - Autocomplete/Label Lookup
|
|
126
|
-
```javascript
|
|
127
|
-
const options = await ws.api.lookup.users({ p_filter: 'ali' });
|
|
128
|
-
// Returns: [{ label: 'Alice', value: 1 }]
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### SEARCH - Advanced Search with Pagination
|
|
132
|
-
```javascript
|
|
133
|
-
const results = await ws.api.search.users({
|
|
134
|
-
filters: {
|
|
135
|
-
name: { ilike: '%alice%' },
|
|
136
|
-
email: 'alice@example.com',
|
|
137
|
-
created_at: { gte: '2025-01-01' }
|
|
138
|
-
},
|
|
139
|
-
sort: { field: 'name', order: 'asc' },
|
|
140
|
-
page: 1,
|
|
141
|
-
limit: 25
|
|
142
|
-
});
|
|
143
|
-
// Returns: { data: [...], total: 42, page: 1, limit: 25 }
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## Entity Registration
|
|
147
|
-
|
|
148
|
-
Before DZQL works with a table, register it:
|
|
149
|
-
|
|
150
|
-
```sql
|
|
151
|
-
SELECT dzql.register_entity(
|
|
152
|
-
p_table_name := 'users',
|
|
153
|
-
p_label_field := 'name', -- For LOOKUP display
|
|
154
|
-
p_searchable_fields := array['name', 'email'], -- For SEARCH
|
|
155
|
-
p_fk_includes := '{"department": "departments"}'::jsonb, -- Dereference FKs
|
|
156
|
-
p_graph_rules := '{
|
|
157
|
-
"on_delete": {
|
|
158
|
-
"cascade": {
|
|
159
|
-
"actions": [{
|
|
160
|
-
"type": "delete",
|
|
161
|
-
"entity": "posts",
|
|
162
|
-
"condition": "user_id = @id"
|
|
163
|
-
}]
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}'::jsonb
|
|
167
|
-
);
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Core API
|
|
171
|
-
|
|
172
|
-
### Server-Side (Bun/Node)
|
|
173
|
-
|
|
174
|
-
```javascript
|
|
175
|
-
import { createServer, db, sql } from 'dzql';
|
|
176
|
-
|
|
177
|
-
// Direct SQL access
|
|
178
|
-
const users = await sql`SELECT * FROM users WHERE active = true`;
|
|
179
|
-
|
|
180
|
-
// DZQL operations (require userId for permissions)
|
|
181
|
-
const user = await db.api.get.users({ id: 1 }, userId);
|
|
182
|
-
const saved = await db.api.save.users({ name: 'Bob' }, userId);
|
|
183
|
-
const searched = await db.api.search.users(
|
|
184
|
-
{ filters: { name: 'bob' } },
|
|
185
|
-
userId
|
|
186
|
-
);
|
|
187
|
-
const deleted = await db.api.delete.users({ id: 1 }, userId);
|
|
188
|
-
const options = await db.api.lookup.users({ p_filter: 'bo' }, userId);
|
|
189
|
-
|
|
190
|
-
// Custom functions
|
|
191
|
-
const result = await db.api.myCustomFunction({ param: 'value' }, userId);
|
|
192
|
-
|
|
193
|
-
// Start server
|
|
194
|
-
const server = createServer({
|
|
195
|
-
port: 3000,
|
|
196
|
-
customApi: {}, // Optional: add custom functions
|
|
197
|
-
staticPath: './public', // Optional: serve static files
|
|
198
|
-
routes: { // Optional: standard HTTP routes
|
|
199
|
-
'/health': () => new Response('OK')
|
|
200
|
-
},
|
|
201
|
-
onReady: async (broadcast) => { // Optional: routes needing broadcast
|
|
202
|
-
return {
|
|
203
|
-
'/mcp': createMCPRoute(broadcast) // Example: MCP integration
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Client-Side (Browser/Bun)
|
|
210
|
-
|
|
211
|
-
```javascript
|
|
212
|
-
import { WebSocketManager } from 'dzql/client';
|
|
213
|
-
|
|
214
|
-
// Create connection
|
|
215
|
-
const ws = new WebSocketManager();
|
|
216
|
-
await ws.connect();
|
|
217
|
-
|
|
218
|
-
// Authentication
|
|
219
|
-
const auth = await ws.api.login_user({
|
|
220
|
-
email: 'user@example.com',
|
|
221
|
-
password: 'password'
|
|
222
|
-
});
|
|
223
|
-
// Returns: { token, profile, user_id }
|
|
224
|
-
|
|
225
|
-
// All DZQL operations
|
|
226
|
-
const user = await ws.api.get.users({ id: 1 });
|
|
227
|
-
const saved = await ws.api.save.users({ name: 'Charlie' });
|
|
228
|
-
const deleted = await ws.api.delete.users({ id: 1 });
|
|
229
|
-
const lookup = await ws.api.lookup.users({ p_filter: 'char' });
|
|
230
|
-
const search = await ws.api.search.users({ filters: {} });
|
|
231
|
-
|
|
232
|
-
// Custom functions
|
|
233
|
-
const result = await ws.api.myCustomFunction({ foo: 'bar' });
|
|
234
|
-
|
|
235
|
-
// Real-time events
|
|
236
|
-
const unsubscribe = ws.onBroadcast((method, params) => {
|
|
237
|
-
console.log(`${method}:`, params.data);
|
|
238
|
-
// Events: "users:insert", "users:update", "users:delete"
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Cleanup
|
|
242
|
-
ws.cleanDisconnect();
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
## Custom Functions
|
|
246
|
-
|
|
247
|
-
Add functions alongside DZQL operations:
|
|
248
|
-
|
|
249
|
-
### PostgreSQL Function
|
|
250
|
-
```sql
|
|
251
|
-
CREATE OR REPLACE FUNCTION transfer_amount(
|
|
252
|
-
p_user_id INT,
|
|
253
|
-
p_from_account INT,
|
|
254
|
-
p_to_account INT,
|
|
255
|
-
p_amount DECIMAL
|
|
256
|
-
) RETURNS TABLE (success BOOLEAN, message TEXT) AS $$
|
|
257
|
-
BEGIN
|
|
258
|
-
-- Your logic here
|
|
259
|
-
RETURN QUERY SELECT true, 'Transfer complete';
|
|
260
|
-
END;
|
|
261
|
-
$$ LANGUAGE plpgsql;
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### Bun Function
|
|
265
|
-
```javascript
|
|
266
|
-
// server/api.js
|
|
267
|
-
export async function transfer_amount(userId, params) {
|
|
268
|
-
const { from_account, to_account, amount } = params;
|
|
269
|
-
// Your logic here
|
|
270
|
-
return { success: true, message: 'Transfer complete' };
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// server/index.js
|
|
274
|
-
const customApi = await import('./api.js');
|
|
275
|
-
const server = createServer({ customApi });
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
### Usage
|
|
279
|
-
```javascript
|
|
280
|
-
const result = await ws.api.transfer_amount({
|
|
281
|
-
from_account: 1,
|
|
282
|
-
to_account: 2,
|
|
283
|
-
amount: 100
|
|
284
|
-
});
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Real-time Events
|
|
288
|
-
|
|
289
|
-
Listen for database changes in real-time:
|
|
290
|
-
|
|
291
|
-
```javascript
|
|
292
|
-
ws.onBroadcast((method, params) => {
|
|
293
|
-
if (method === 'users:insert') {
|
|
294
|
-
console.log('New user:', params.data);
|
|
295
|
-
// params: { op: 'insert', table: 'users', data: {...}, notify_users: [...] }
|
|
296
|
-
}
|
|
297
|
-
if (method === 'users:update') {
|
|
298
|
-
console.log('User updated:', params.data);
|
|
299
|
-
}
|
|
300
|
-
if (method === 'users:delete') {
|
|
301
|
-
console.log('User deleted:', params.data);
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
## Graph Rules - Cascading Operations
|
|
307
|
-
|
|
308
|
-
Automatically cascade changes through relationships:
|
|
309
|
-
|
|
310
|
-
```sql
|
|
311
|
-
SELECT dzql.register_entity(
|
|
312
|
-
p_table_name := 'organisations',
|
|
313
|
-
p_label_field := 'name',
|
|
314
|
-
p_searchable_fields := array['name'],
|
|
315
|
-
p_graph_rules := '{
|
|
316
|
-
"on_delete": {
|
|
317
|
-
"cascade_to_teams": {
|
|
318
|
-
"actions": [{
|
|
319
|
-
"type": "delete",
|
|
320
|
-
"entity": "teams",
|
|
321
|
-
"condition": "org_id = @id"
|
|
322
|
-
}]
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
"on_create": {
|
|
326
|
-
"create_default_team": {
|
|
327
|
-
"actions": [{
|
|
328
|
-
"type": "create",
|
|
329
|
-
"entity": "teams",
|
|
330
|
-
"data": {
|
|
331
|
-
"org_id": "@id",
|
|
332
|
-
"name": "Default Team"
|
|
333
|
-
}
|
|
334
|
-
}]
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}'::jsonb
|
|
338
|
-
);
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
Available actions: `create`, `update`, `delete`, `insert`, `call_function`
|
|
342
|
-
|
|
343
|
-
## Permissions & Row-Level Security
|
|
344
|
-
|
|
345
|
-
Implement permissions in your entity registration:
|
|
346
|
-
|
|
347
|
-
```sql
|
|
348
|
-
SELECT dzql.register_entity(
|
|
349
|
-
p_table_name := 'posts',
|
|
350
|
-
p_label_field := 'title',
|
|
351
|
-
p_searchable_fields := array['title', 'content'],
|
|
352
|
-
p_permission_rules := '{
|
|
353
|
-
"view": {
|
|
354
|
-
"public_posts": {
|
|
355
|
-
"condition": "public = true OR author_id = @user_id"
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
"edit": {
|
|
359
|
-
"own_posts": {
|
|
360
|
-
"condition": "author_id = @user_id"
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}'::jsonb
|
|
364
|
-
);
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
## Search Filter Operators
|
|
368
|
-
|
|
369
|
-
```javascript
|
|
370
|
-
const results = await ws.api.search.venues({
|
|
371
|
-
filters: {
|
|
372
|
-
// Exact match
|
|
373
|
-
name: 'Madison Square Garden',
|
|
374
|
-
|
|
375
|
-
// Comparison operators
|
|
376
|
-
capacity: { gt: 1000 }, // Greater than
|
|
377
|
-
capacity: { gte: 1000 }, // Greater or equal
|
|
378
|
-
capacity: { lt: 50000 }, // Less than
|
|
379
|
-
capacity: { lte: 50000 }, // Less or equal
|
|
380
|
-
capacity: { neq: 5000 }, // Not equal
|
|
381
|
-
|
|
382
|
-
// Range
|
|
383
|
-
capacity: { between: [1000, 50000] },
|
|
384
|
-
|
|
385
|
-
// Pattern matching
|
|
386
|
-
name: { like: '%garden%' }, // Case-sensitive
|
|
387
|
-
name: { ilike: '%GARDEN%' }, // Case-insensitive
|
|
388
|
-
|
|
389
|
-
// NULL checks
|
|
390
|
-
description: null, // IS NULL
|
|
391
|
-
description: { not_null: true }, // IS NOT NULL
|
|
392
|
-
|
|
393
|
-
// Arrays
|
|
394
|
-
categories: ['sports', 'music'], // IN array
|
|
395
|
-
categories: { not_in: ['adult'] }, // NOT IN array
|
|
396
|
-
|
|
397
|
-
// Text search (across searchable_fields)
|
|
398
|
-
_search: 'madison garden'
|
|
399
|
-
},
|
|
400
|
-
page: 1,
|
|
401
|
-
limit: 25
|
|
402
|
-
});
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
## Project Structure
|
|
406
|
-
|
|
407
|
-
```
|
|
408
|
-
my-app/
|
|
409
|
-
├── server/
|
|
410
|
-
│ ├── index.js # Server entry point
|
|
411
|
-
│ └── api.js # Custom API functions (optional)
|
|
412
|
-
├── database/
|
|
413
|
-
│ ├── docker-compose.yml # PostgreSQL setup
|
|
414
|
-
│ ├── init_db/
|
|
415
|
-
│ │ ├── 001_schema.sql # Your tables
|
|
416
|
-
│ │ └── 002_entities.sql # Entity registration
|
|
417
|
-
│ └── seeds/ # Sample data (optional)
|
|
418
|
-
├── client/
|
|
419
|
-
│ └── index.html # Frontend (optional)
|
|
420
|
-
├── tests/
|
|
421
|
-
│ └── app.test.js
|
|
422
|
-
├── package.json
|
|
423
|
-
└── bunfig.toml # Bun config (optional)
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
## Environment Variables
|
|
427
|
-
|
|
428
|
-
```bash
|
|
429
|
-
# Database
|
|
430
|
-
DATABASE_URL=postgresql://dzql:dzql@localhost:5432/dzql
|
|
431
|
-
|
|
432
|
-
# Server
|
|
433
|
-
PORT=3000
|
|
434
|
-
NODE_ENV=development
|
|
435
|
-
|
|
436
|
-
# JWT
|
|
437
|
-
JWT_SECRET=your-secret-key-min-32-chars
|
|
438
|
-
JWT_EXPIRES_IN=7d
|
|
439
|
-
|
|
440
|
-
# WebSocket
|
|
441
|
-
WS_PING_INTERVAL=30000 # Keep-alive ping (Heroku safe: <55s)
|
|
442
|
-
WS_PING_TIMEOUT=5000
|
|
443
|
-
|
|
444
|
-
# Logging
|
|
445
|
-
LOG_LEVEL=INFO # ERROR, WARN, INFO, DEBUG, TRACE
|
|
446
|
-
LOG_CATEGORIES=ws:debug,db:debug
|
|
32
|
+
const user = await ws.api.save.users({ name: 'Alice' });
|
|
33
|
+
const results = await ws.api.search.users({ filters: { name: 'alice' } });
|
|
447
34
|
```
|
|
448
35
|
|
|
449
|
-
##
|
|
450
|
-
|
|
451
|
-
See the [venues example](https://github.com/blueshed/dzql/tree/main/packages/venues) for a complete working application.
|
|
452
|
-
|
|
453
|
-
### Todo App
|
|
454
|
-
```javascript
|
|
455
|
-
// Schema
|
|
456
|
-
CREATE TABLE todos (
|
|
457
|
-
id SERIAL PRIMARY KEY,
|
|
458
|
-
user_id INT REFERENCES users(id),
|
|
459
|
-
title TEXT NOT NULL,
|
|
460
|
-
completed BOOLEAN DEFAULT FALSE,
|
|
461
|
-
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
SELECT dzql.register_entity('todos', 'title', array['title']);
|
|
465
|
-
|
|
466
|
-
// Client
|
|
467
|
-
const todo = await ws.api.save.todos({ title: 'Learn DZQL' });
|
|
468
|
-
const list = await ws.api.search.todos({
|
|
469
|
-
filters: { completed: false },
|
|
470
|
-
limit: 100
|
|
471
|
-
});
|
|
472
|
-
await ws.api.save.todos({ id: todo.id, completed: true });
|
|
473
|
-
await ws.api.delete.todos({ id: todo.id });
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## Error Handling
|
|
36
|
+
## License
|
|
477
37
|
|
|
478
|
-
|
|
479
|
-
try {
|
|
480
|
-
const user = await ws.api.get.users({ id: 999 });
|
|
481
|
-
} catch (error) {
|
|
482
|
-
// Common errors:
|
|
483
|
-
// "record not found" - Record doesn't exist
|
|
484
|
-
// "Permission denied: view on users" - Access denied
|
|
485
|
-
// "entity users not configured" - Entity not registered
|
|
486
|
-
// "Column foo does not exist in table users" - Invalid column
|
|
487
|
-
console.error(error.message);
|
|
488
|
-
}
|
|
489
|
-
```
|
|
38
|
+
MIT
|
|
490
39
|
|
|
491
|
-
##
|
|
40
|
+
## Links
|
|
492
41
|
|
|
493
|
-
- **Documentation**: [GETTING_STARTED.md](./GETTING_STARTED.md)
|
|
494
42
|
- **GitHub**: https://github.com/blueshed/dzql
|
|
495
43
|
- **Issues**: https://github.com/blueshed/dzql/issues
|
|
496
|
-
- **
|
|
497
|
-
|
|
498
|
-
## License
|
|
499
|
-
|
|
500
|
-
MIT - See LICENSE file
|
|
501
|
-
|
|
502
|
-
## Authors
|
|
503
|
-
|
|
504
|
-
Created by [Blueshed](https://blueshed.com)
|
|
505
|
-
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
**Ready to build?** Start with [GETTING_STARTED.md](./GETTING_STARTED.md) 🚀
|
|
44
|
+
- **npm**: https://www.npmjs.com/package/dzql
|