@devbro/neko-sql 0.1.36 → 0.1.37
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 +541 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,33 +1,45 @@
|
|
|
1
1
|
# @devbro/neko-sql
|
|
2
2
|
|
|
3
|
-
SQL
|
|
3
|
+
A powerful, type-safe SQL query builder and database abstraction layer for Node.js and TypeScript. Build queries programmatically with a fluent API and support for multiple database engines.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @devbro/neko-sql
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🎯 **Unified API** - Same interface for PostgreSQL, MySQL, and SQLite
|
|
14
|
+
- 🔨 **Query Builder** - Fluent, chainable API for building SQL queries
|
|
15
|
+
- 🛡️ **Type-Safe** - Full TypeScript support with type inference
|
|
16
|
+
- 🔄 **Transactions** - Complete transaction support with rollback
|
|
17
|
+
- 🔗 **Joins** - Support for all join types (inner, left, right, cross)
|
|
18
|
+
- 📊 **Schema Builder** - Create and modify database schemas
|
|
19
|
+
- 🚀 **Migration Ready** - Built-in support for database migrations
|
|
20
|
+
- ⚡ **Async/Await** - Modern promise-based API
|
|
21
|
+
- 🔒 **SQL Injection Protection** - Parameterized queries by default
|
|
11
22
|
|
|
12
|
-
##
|
|
23
|
+
## Supported Databases
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
### Currently Supported
|
|
15
26
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
27
|
+
- ✅ **PostgreSQL** - v9.5+
|
|
28
|
+
- ✅ **MySQL** - v5.7+ / MariaDB v10.2+
|
|
29
|
+
- ✅ **SQLite** - v3.8+
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
### Planned Support
|
|
21
32
|
|
|
22
|
-
-
|
|
33
|
+
- 🔜 **Microsoft SQL Server** (MSSQL)
|
|
23
34
|
|
|
24
|
-
##
|
|
35
|
+
## Quick Start
|
|
25
36
|
|
|
26
|
-
### PostgreSQL
|
|
37
|
+
### PostgreSQL
|
|
27
38
|
|
|
28
39
|
```typescript
|
|
29
40
|
import { PostgresqlConnection } from '@devbro/neko-sql';
|
|
30
41
|
|
|
42
|
+
// Create connection
|
|
31
43
|
const conn = new PostgresqlConnection({
|
|
32
44
|
host: 'localhost',
|
|
33
45
|
database: 'mydb',
|
|
@@ -36,34 +48,37 @@ const conn = new PostgresqlConnection({
|
|
|
36
48
|
port: 5432,
|
|
37
49
|
});
|
|
38
50
|
|
|
51
|
+
// Connect to database
|
|
39
52
|
await conn.connect();
|
|
40
53
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
|
|
54
|
+
// Execute a simple query
|
|
55
|
+
const users = await conn.getQuery().table('users').whereOp('status', '=', 'active').get();
|
|
56
|
+
|
|
57
|
+
console.log(users);
|
|
44
58
|
|
|
59
|
+
// Don't forget to disconnect
|
|
45
60
|
await conn.disconnect();
|
|
46
61
|
```
|
|
47
62
|
|
|
48
|
-
### SQLite
|
|
63
|
+
### SQLite
|
|
49
64
|
|
|
50
65
|
```typescript
|
|
51
66
|
import { SqliteConnection } from '@devbro/neko-sql';
|
|
52
67
|
|
|
53
68
|
const conn = new SqliteConnection({
|
|
54
69
|
filename: './database.db',
|
|
70
|
+
// Or use in-memory database
|
|
71
|
+
// filename: ':memory:',
|
|
55
72
|
});
|
|
56
73
|
|
|
57
74
|
await conn.connect();
|
|
58
75
|
|
|
59
|
-
|
|
60
|
-
const query = conn.getQuery();
|
|
61
|
-
const results = await query.table('users').whereOp('id', '=', 1).get();
|
|
76
|
+
const users = await conn.getQuery().table('users').whereOp('age', '>', 18).get();
|
|
62
77
|
|
|
63
78
|
await conn.disconnect();
|
|
64
79
|
```
|
|
65
80
|
|
|
66
|
-
### MySQL
|
|
81
|
+
### MySQL
|
|
67
82
|
|
|
68
83
|
```typescript
|
|
69
84
|
import { MysqlConnection } from '@devbro/neko-sql';
|
|
@@ -78,13 +93,511 @@ const conn = new MysqlConnection({
|
|
|
78
93
|
|
|
79
94
|
await conn.connect();
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
96
|
+
const activeUsers = await conn
|
|
97
|
+
.getQuery()
|
|
98
|
+
.table('users')
|
|
99
|
+
.whereOp('is_active', '=', true)
|
|
100
|
+
.orderBy('created_at', 'desc')
|
|
101
|
+
.limit(10)
|
|
102
|
+
.get();
|
|
84
103
|
|
|
85
104
|
await conn.disconnect();
|
|
86
105
|
```
|
|
87
106
|
|
|
88
|
-
|
|
107
|
+
## Query Builder API
|
|
108
|
+
|
|
109
|
+
### Basic Queries
|
|
110
|
+
|
|
111
|
+
#### Select
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const query = conn.getQuery();
|
|
115
|
+
|
|
116
|
+
// Select all columns
|
|
117
|
+
const allUsers = await query.table('users').get();
|
|
118
|
+
|
|
119
|
+
// Select specific columns
|
|
120
|
+
const users = await query.table('users').select(['id', 'name', 'email']).get();
|
|
121
|
+
|
|
122
|
+
// Select first record
|
|
123
|
+
const user = await query.table('users').whereOp('id', '=', 1).first();
|
|
124
|
+
|
|
125
|
+
// Count records
|
|
126
|
+
const count = await query.table('users').whereOp('status', '=', 'active').count();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Insert
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Insert single record
|
|
133
|
+
await query.table('users').insert({
|
|
134
|
+
name: 'John Doe',
|
|
135
|
+
email: 'john@example.com',
|
|
136
|
+
age: 30,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Insert and get ID
|
|
140
|
+
const userId = await query.table('users').insertGetId({
|
|
141
|
+
name: 'Jane Smith',
|
|
142
|
+
email: 'jane@example.com',
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Batch insert (multiple records)
|
|
146
|
+
await query.table('users').insert([
|
|
147
|
+
{ name: 'User 1', email: 'user1@example.com' },
|
|
148
|
+
{ name: 'User 2', email: 'user2@example.com' },
|
|
149
|
+
{ name: 'User 3', email: 'user3@example.com' },
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Batch insert and get IDs
|
|
153
|
+
const ids = await query.table('users').insertGetId([
|
|
154
|
+
{ name: 'User A', email: 'a@example.com' },
|
|
155
|
+
{ name: 'User B', email: 'b@example.com' },
|
|
156
|
+
]);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### Update
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Update records
|
|
163
|
+
await query.table('users').whereOp('id', '=', 1).update({
|
|
164
|
+
name: 'Updated Name',
|
|
165
|
+
updated_at: new Date(),
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Update multiple records
|
|
169
|
+
await query.table('users').whereOp('status', '=', 'pending').update({ status: 'active' });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Delete
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Delete specific record
|
|
176
|
+
await query.table('users').whereOp('id', '=', 1).delete();
|
|
177
|
+
|
|
178
|
+
// Delete multiple records
|
|
179
|
+
await query.table('users').whereOp('status', '=', 'inactive').delete();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Where Clauses
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Basic where
|
|
186
|
+
query.whereOp('age', '>', 18);
|
|
187
|
+
query.whereOp('status', '=', 'active');
|
|
188
|
+
query.whereOp('email', 'LIKE', '%@example.com');
|
|
189
|
+
|
|
190
|
+
// Multiple where conditions (AND)
|
|
191
|
+
query.whereOp('age', '>', 18).whereOp('status', '=', 'active');
|
|
192
|
+
|
|
193
|
+
// Or where
|
|
194
|
+
query.whereOp('role', '=', 'admin').orWhereOp('role', '=', 'moderator');
|
|
195
|
+
|
|
196
|
+
// Where in
|
|
197
|
+
query.whereIn('status', ['active', 'pending', 'approved']);
|
|
198
|
+
|
|
199
|
+
// Where not in
|
|
200
|
+
query.whereNotIn('status', ['banned', 'deleted']);
|
|
201
|
+
|
|
202
|
+
// Where null
|
|
203
|
+
query.whereNull('deleted_at');
|
|
204
|
+
|
|
205
|
+
// Where not null
|
|
206
|
+
query.whereNotNull('email_verified_at');
|
|
207
|
+
|
|
208
|
+
// Where between
|
|
209
|
+
query.whereBetween('age', [18, 65]);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Joins
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Inner join
|
|
216
|
+
const results = await query
|
|
217
|
+
.table('users')
|
|
218
|
+
.join('posts', 'users.id', '=', 'posts.user_id')
|
|
219
|
+
.select(['users.name', 'posts.title'])
|
|
220
|
+
.get();
|
|
221
|
+
|
|
222
|
+
// Left join
|
|
223
|
+
const users = await query
|
|
224
|
+
.table('users')
|
|
225
|
+
.leftJoin('profiles', 'users.id', '=', 'profiles.user_id')
|
|
226
|
+
.get();
|
|
227
|
+
|
|
228
|
+
// Right join
|
|
229
|
+
const data = await query
|
|
230
|
+
.table('orders')
|
|
231
|
+
.rightJoin('customers', 'orders.customer_id', '=', 'customers.id')
|
|
232
|
+
.get();
|
|
233
|
+
|
|
234
|
+
// Multiple joins
|
|
235
|
+
const results = await query
|
|
236
|
+
.table('users')
|
|
237
|
+
.join('posts', 'users.id', '=', 'posts.user_id')
|
|
238
|
+
.join('categories', 'posts.category_id', '=', 'categories.id')
|
|
239
|
+
.select(['users.name', 'posts.title', 'categories.name as category'])
|
|
240
|
+
.get();
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Ordering and Limiting
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// Order by
|
|
247
|
+
query.orderBy('created_at', 'desc');
|
|
248
|
+
query.orderBy('name', 'asc');
|
|
249
|
+
|
|
250
|
+
// Multiple order by
|
|
251
|
+
query.orderBy('status', 'asc').orderBy('created_at', 'desc');
|
|
252
|
+
|
|
253
|
+
// Limit
|
|
254
|
+
query.limit(10);
|
|
255
|
+
|
|
256
|
+
// Offset
|
|
257
|
+
query.offset(20);
|
|
258
|
+
|
|
259
|
+
// Pagination
|
|
260
|
+
query.limit(10).offset(20); // Page 3, 10 items per page
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Grouping and Aggregates
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// Group by
|
|
267
|
+
const results = await query
|
|
268
|
+
.table('orders')
|
|
269
|
+
.select(['status', 'COUNT(*) as count'])
|
|
270
|
+
.groupBy('status')
|
|
271
|
+
.get();
|
|
272
|
+
|
|
273
|
+
// Having clause
|
|
274
|
+
const data = await query
|
|
275
|
+
.table('users')
|
|
276
|
+
.select(['country', 'COUNT(*) as user_count'])
|
|
277
|
+
.groupBy('country')
|
|
278
|
+
.having('user_count', '>', 10)
|
|
279
|
+
.get();
|
|
280
|
+
|
|
281
|
+
// Aggregate functions
|
|
282
|
+
const totalUsers = await query.table('users').count();
|
|
283
|
+
const avgAge = await query.table('users').avg('age');
|
|
284
|
+
const maxPrice = await query.table('products').max('price');
|
|
285
|
+
const minPrice = await query.table('products').min('price');
|
|
286
|
+
const totalRevenue = await query.table('orders').sum('total');
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Raw Queries
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// Execute raw SQL
|
|
293
|
+
const results = await conn.raw('SELECT * FROM users WHERE age > ?', [18]);
|
|
294
|
+
|
|
295
|
+
// Raw where clause
|
|
296
|
+
query.table('users').whereRaw('DATE(created_at) = CURDATE()');
|
|
297
|
+
|
|
298
|
+
// Raw select
|
|
299
|
+
query.table('users').selectRaw('COUNT(*) as total, AVG(age) as average_age');
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Schema Builder
|
|
303
|
+
|
|
304
|
+
### Creating Tables
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { Schema, Blueprint } from '@devbro/neko-sql';
|
|
308
|
+
|
|
309
|
+
const schema = new Schema(conn, conn.getSchemaGrammar());
|
|
310
|
+
|
|
311
|
+
// Create a new table
|
|
312
|
+
await schema.createTable('users', (table: Blueprint) => {
|
|
313
|
+
table.id(); // Auto-incrementing primary key
|
|
314
|
+
table.string('name', 255);
|
|
315
|
+
table.string('email', 255).unique();
|
|
316
|
+
table.string('password');
|
|
317
|
+
table.integer('age').nullable();
|
|
318
|
+
table.boolean('is_active').default(true);
|
|
319
|
+
table.text('bio').nullable();
|
|
320
|
+
table.timestamps(); // created_at and updated_at
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Create table with foreign key
|
|
324
|
+
await schema.createTable('posts', (table: Blueprint) => {
|
|
325
|
+
table.id();
|
|
326
|
+
table.string('title');
|
|
327
|
+
table.text('content');
|
|
328
|
+
table.integer('user_id').unsigned();
|
|
329
|
+
table.foreign('user_id').references('id').on('users').onDelete('cascade');
|
|
330
|
+
table.timestamps();
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Modifying Tables
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
// Add columns
|
|
338
|
+
await schema.alterTable('users', (table: Blueprint) => {
|
|
339
|
+
table.string('phone', 20).nullable();
|
|
340
|
+
table.timestamp('last_login').nullable();
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Drop columns
|
|
344
|
+
await schema.alterTable('users', (table: Blueprint) => {
|
|
345
|
+
table.dropColumn('phone');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Rename column
|
|
349
|
+
await schema.alterTable('users', (table: Blueprint) => {
|
|
350
|
+
table.renameColumn('name', 'full_name');
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Dropping Tables
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
// Drop table if exists
|
|
358
|
+
await schema.dropTableIfExists('users');
|
|
359
|
+
|
|
360
|
+
// Drop table
|
|
361
|
+
await schema.dropTable('users');
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Available Column Types
|
|
365
|
+
|
|
366
|
+
```typescript
|
|
367
|
+
table.id(); // Auto-incrementing ID
|
|
368
|
+
table.integer('column'); // Integer
|
|
369
|
+
table.bigInteger('column'); // Big integer
|
|
370
|
+
table.string('column', 255); // VARCHAR
|
|
371
|
+
table.text('column'); // TEXT
|
|
372
|
+
table.boolean('column'); // Boolean
|
|
373
|
+
table.date('column'); // Date
|
|
374
|
+
table.datetime('column'); // Datetime
|
|
375
|
+
table.timestamp('column'); // Timestamp
|
|
376
|
+
table.json('column'); // JSON
|
|
377
|
+
table.decimal('column', 10, 2); // Decimal
|
|
378
|
+
table.float('column'); // Float
|
|
379
|
+
table.double('column'); // Double
|
|
380
|
+
|
|
381
|
+
// Modifiers
|
|
382
|
+
column.nullable(); // Allow NULL
|
|
383
|
+
column.default(value); // Set default value
|
|
384
|
+
column.unique(); // Add unique constraint
|
|
385
|
+
column.unsigned(); // Unsigned (for numbers)
|
|
386
|
+
column.primary(); // Set as primary key
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Transactions
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
// Using transactions
|
|
393
|
+
await conn.beginTransaction();
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
// Execute queries
|
|
397
|
+
await conn.getQuery().table('users').insert({ name: 'John' });
|
|
398
|
+
await conn.getQuery().table('logs').insert({ action: 'user_created' });
|
|
399
|
+
|
|
400
|
+
// Commit if successful
|
|
401
|
+
await conn.commit();
|
|
402
|
+
} catch (error) {
|
|
403
|
+
// Rollback on error
|
|
404
|
+
await conn.rollback();
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Database Management
|
|
410
|
+
|
|
411
|
+
### Create/Drop Database
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Check if database exists
|
|
415
|
+
const exists = await conn.existsDatabase('mydb');
|
|
416
|
+
|
|
417
|
+
// Create database
|
|
418
|
+
await conn.createDatabase('mydb');
|
|
419
|
+
|
|
420
|
+
// Drop database
|
|
421
|
+
await conn.dropDatabase('mydb');
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Connection Pooling
|
|
425
|
+
|
|
426
|
+
For production applications, use connection pooling:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { PostgresqlConnection } from '@devbro/neko-sql';
|
|
430
|
+
|
|
431
|
+
const conn = new PostgresqlConnection({
|
|
432
|
+
host: 'localhost',
|
|
433
|
+
database: 'mydb',
|
|
434
|
+
user: 'myuser',
|
|
435
|
+
password: 'mypassword',
|
|
436
|
+
port: 5432,
|
|
437
|
+
// Connection pool settings
|
|
438
|
+
max: 20, // Maximum number of connections
|
|
439
|
+
min: 5, // Minimum number of connections
|
|
440
|
+
idleTimeoutMillis: 30000,
|
|
441
|
+
connectionTimeoutMillis: 2000,
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Best Practices
|
|
446
|
+
|
|
447
|
+
1. **Always Close Connections** - Use `await conn.disconnect()` when done
|
|
448
|
+
2. **Use Transactions** - For multiple related operations
|
|
449
|
+
3. **Parameterized Queries** - Use the query builder or raw queries with parameters
|
|
450
|
+
4. **Connection Pooling** - Enable pooling in production
|
|
451
|
+
5. **Error Handling** - Wrap database operations in try-catch blocks
|
|
452
|
+
6. **Environment Variables** - Store credentials in environment variables
|
|
453
|
+
7. **Migrations** - Use schema builder for database versioning
|
|
454
|
+
|
|
455
|
+
## Advanced Example
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
import { PostgresqlConnection, Schema, Blueprint } from '@devbro/neko-sql';
|
|
459
|
+
|
|
460
|
+
async function main() {
|
|
461
|
+
const conn = new PostgresqlConnection({
|
|
462
|
+
host: process.env.DB_HOST,
|
|
463
|
+
database: process.env.DB_NAME,
|
|
464
|
+
user: process.env.DB_USER,
|
|
465
|
+
password: process.env.DB_PASSWORD,
|
|
466
|
+
port: parseInt(process.env.DB_PORT || '5432'),
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
try {
|
|
470
|
+
await conn.connect();
|
|
471
|
+
|
|
472
|
+
// Create schema
|
|
473
|
+
const schema = new Schema(conn, conn.getSchemaGrammar());
|
|
474
|
+
|
|
475
|
+
await schema.createTable('users', (table: Blueprint) => {
|
|
476
|
+
table.id();
|
|
477
|
+
table.string('email', 250).unique();
|
|
478
|
+
table.string('first_name').default('');
|
|
479
|
+
table.timestamps();
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Insert data
|
|
483
|
+
const userId = await conn.getQuery().table('users').insertGetId({
|
|
484
|
+
email: 'john@example.com',
|
|
485
|
+
first_name: 'John',
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// Query with joins
|
|
489
|
+
const results = await conn
|
|
490
|
+
.getQuery()
|
|
491
|
+
.table('users')
|
|
492
|
+
.leftJoin('profiles', 'users.id', '=', 'profiles.user_id')
|
|
493
|
+
.whereOp('users.id', '=', userId)
|
|
494
|
+
.select(['users.*', 'profiles.bio'])
|
|
495
|
+
.first();
|
|
496
|
+
|
|
497
|
+
console.log('User:', results);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
console.error('Database error:', error);
|
|
500
|
+
} finally {
|
|
501
|
+
await conn.disconnect();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
main();
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
## TypeScript Support
|
|
509
|
+
|
|
510
|
+
Full TypeScript definitions included:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
import { Connection, Query, Schema, Blueprint } from '@devbro/neko-sql';
|
|
514
|
+
|
|
515
|
+
// Type-safe queries
|
|
516
|
+
interface User {
|
|
517
|
+
id: number;
|
|
518
|
+
name: string;
|
|
519
|
+
email: string;
|
|
520
|
+
created_at: Date;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const users = await conn.getQuery().table<User>('users').get();
|
|
524
|
+
|
|
525
|
+
// users is typed as User[]
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Troubleshooting
|
|
529
|
+
|
|
530
|
+
### Connection Issues
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
// Test connection
|
|
534
|
+
try {
|
|
535
|
+
await conn.connect();
|
|
536
|
+
console.log('Connected successfully');
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error('Connection failed:', error.message);
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Debugging Queries
|
|
543
|
+
|
|
544
|
+
Enable query logging to see generated SQL:
|
|
545
|
+
|
|
546
|
+
```typescript
|
|
547
|
+
// Log executed queries (implementation may vary)
|
|
548
|
+
conn.on('query', (sql) => {
|
|
549
|
+
console.log('Executed:', sql);
|
|
550
|
+
});
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## Migration Example
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// migrations/001_create_users_table.ts
|
|
557
|
+
import { Schema, Blueprint, Connection } from '@devbro/neko-sql';
|
|
558
|
+
|
|
559
|
+
export async function up(conn: Connection) {
|
|
560
|
+
const schema = new Schema(conn, conn.getSchemaGrammar());
|
|
561
|
+
|
|
562
|
+
await schema.createTable('users', (table: Blueprint) => {
|
|
563
|
+
table.id();
|
|
564
|
+
table.string('email').unique();
|
|
565
|
+
table.string('password');
|
|
566
|
+
table.timestamps();
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export async function down(conn: Connection) {
|
|
571
|
+
const schema = new Schema(conn, conn.getSchemaGrammar());
|
|
572
|
+
await schema.dropTable('users');
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## Examples and Tests
|
|
577
|
+
|
|
578
|
+
For more comprehensive examples, check the test files in the repository:
|
|
579
|
+
|
|
580
|
+
- `/tests/query.test.ts` - Query builder examples
|
|
581
|
+
- `/tests/schema.test.ts` - Schema builder examples
|
|
582
|
+
- `/tests/transactions.test.ts` - Transaction examples
|
|
583
|
+
|
|
584
|
+
## Contributing
|
|
585
|
+
|
|
586
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
587
|
+
|
|
588
|
+
Areas we'd love help with:
|
|
589
|
+
|
|
590
|
+
- MSSQL support
|
|
591
|
+
- Additional query builder features
|
|
592
|
+
- Performance optimizations
|
|
593
|
+
- Documentation improvements
|
|
594
|
+
|
|
595
|
+
## License
|
|
596
|
+
|
|
597
|
+
MIT
|
|
598
|
+
|
|
599
|
+
## Related Packages
|
|
89
600
|
|
|
90
|
-
|
|
601
|
+
- [@devbro/neko-orm](https://www.npmjs.com/package/@devbro/neko-orm) - Object-relational mapping
|
|
602
|
+
- [@devbro/neko-cache](https://www.npmjs.com/package/@devbro/neko-cache) - Caching solution
|
|
603
|
+
- [@devbro/pashmak](https://www.npmjs.com/package/@devbro/pashmak) - Full-stack TypeScript framework
|