@starbemtech/star-db-query-builder 1.3.0 → 1.4.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/skills/star-db-query-builder/SKILL.md +104 -0
- package/CHANGELOG.md +81 -46
- package/LICENSE +21 -0
- package/README.md +194 -94
- package/bin/install-skill.js +53 -0
- package/dist/src/core/repository.d.ts +92 -9
- package/dist/src/core/repository.js +275 -27
- package/dist/src/core/repository.js.map +1 -1
- package/dist/src/core/types.d.ts +17 -2
- package/dist/src/core/utils.d.ts +102 -0
- package/dist/src/core/utils.js +287 -70
- package/dist/src/core/utils.js.map +1 -1
- package/dist/src/db/initDb.d.ts +60 -50
- package/dist/src/db/initDb.js +96 -64
- package/dist/src/db/initDb.js.map +1 -1
- package/dist/src/db/mysqlClient.d.ts +3 -8
- package/dist/src/db/mysqlClient.js +9 -11
- package/dist/src/db/mysqlClient.js.map +1 -1
- package/dist/src/db/pgClient.js +0 -2
- package/dist/src/db/pgClient.js.map +1 -1
- package/dist/src/monitor/monitor.js +7 -0
- package/dist/src/monitor/monitor.js.map +1 -1
- package/package.json +28 -20
- package/.github/workflows/publish.yml +0 -118
- package/.prettierignore +0 -3
- package/.prettierrc +0 -5
- package/ARCHITECTURE.md +0 -313
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -131
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/mysqlClient.ts.html +0 -685
- package/coverage/lcov-report/pgClient.ts.html +0 -823
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -533
- package/coverage/mysqlClient.ts.html +0 -685
- package/coverage/pgClient.ts.html +0 -823
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/dist/src/setupTests.d.ts +0 -26
- package/dist/src/setupTests.js +0 -43
- package/dist/src/setupTests.js.map +0 -1
- package/docs/INDEX.md +0 -145
- package/docs/methods/findFirst.md +0 -394
- package/docs/methods/findMany.md +0 -587
- package/docs/methods/insert.md +0 -536
- package/docs/methods/insertMany.md +0 -627
- package/docs/methods/joins.md +0 -781
- package/docs/methods/rawQuery.md +0 -284
- package/docs/methods/transactions.md +0 -737
- package/eslint.config.mjs +0 -77
- package/index.ts +0 -16
- package/jest.config.ts +0 -194
- package/scripts/release.sh +0 -123
- package/src/core/repository.ts +0 -865
- package/src/core/types.ts +0 -97
- package/src/core/utils.ts +0 -357
- package/src/db/IDatabaseClient.ts +0 -16
- package/src/db/__tests__/mysqlClient.test.ts +0 -262
- package/src/db/__tests__/pgClient.test.ts +0 -260
- package/src/db/initDb.ts +0 -181
- package/src/db/mysqlClient.ts +0 -200
- package/src/db/pgClient.ts +0 -246
- package/src/monitor/monitor.ts +0 -16
- package/src/setupTests.ts +0 -45
- package/tsconfig.test.json +0 -21
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
# findFirst
|
|
2
|
-
|
|
3
|
-
Finds the first record that matches the specified conditions from a database table.
|
|
4
|
-
|
|
5
|
-
## Signature
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
findFirst<T>({
|
|
9
|
-
tableName: string,
|
|
10
|
-
dbClient: IDatabaseClient,
|
|
11
|
-
select?: string[],
|
|
12
|
-
where?: Conditions<T>,
|
|
13
|
-
groupBy?: string[],
|
|
14
|
-
orderBy?: OrderBy
|
|
15
|
-
}): Promise<T | null>
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Parameters
|
|
19
|
-
|
|
20
|
-
| Parameter | Type | Required | Description |
|
|
21
|
-
| ----------- | ----------------- | -------- | ---------------------------------------------------- |
|
|
22
|
-
| `tableName` | `string` | ✅ | Name of the database table |
|
|
23
|
-
| `dbClient` | `IDatabaseClient` | ✅ | Database client instance |
|
|
24
|
-
| `select` | `string[]` | ❌ | Array of field names to select (default: all fields) |
|
|
25
|
-
| `where` | `Conditions<T>` | ❌ | Conditions to filter records |
|
|
26
|
-
| `groupBy` | `string[]` | ❌ | Fields to group by |
|
|
27
|
-
| `orderBy` | `OrderBy` | ❌ | Sort order specification |
|
|
28
|
-
|
|
29
|
-
## Return Value
|
|
30
|
-
|
|
31
|
-
- **Type**: `Promise<T | null>`
|
|
32
|
-
- **Description**: Returns the first matching record or `null` if no record is found
|
|
33
|
-
|
|
34
|
-
## Examples
|
|
35
|
-
|
|
36
|
-
### Basic Usage
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
import { findFirst } from '@starbemtech/star-db-query-builder'
|
|
40
|
-
|
|
41
|
-
// Find user by email
|
|
42
|
-
const user = await findFirst({
|
|
43
|
-
tableName: 'users',
|
|
44
|
-
dbClient,
|
|
45
|
-
where: {
|
|
46
|
-
email: { operator: '=', value: 'user@example.com' },
|
|
47
|
-
},
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
console.log(user) // { id: 'user-123', name: 'John Doe', email: 'user@example.com', ... }
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### With Specific Fields
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
// Select only specific fields
|
|
57
|
-
const user = await findFirst({
|
|
58
|
-
tableName: 'users',
|
|
59
|
-
dbClient,
|
|
60
|
-
select: ['id', 'name', 'email'],
|
|
61
|
-
where: {
|
|
62
|
-
status: { operator: '=', value: 'active' },
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
console.log(user) // { id: 'user-123', name: 'John Doe', email: 'user@example.com' }
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### With Complex Conditions
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
// Multiple conditions with AND
|
|
73
|
-
const user = await findFirst({
|
|
74
|
-
tableName: 'users',
|
|
75
|
-
dbClient,
|
|
76
|
-
where: {
|
|
77
|
-
AND: [
|
|
78
|
-
{ email: { operator: '=', value: 'user@example.com' } },
|
|
79
|
-
{ status: { operator: '=', value: 'active' } },
|
|
80
|
-
{ verified: { operator: '=', value: true } },
|
|
81
|
-
],
|
|
82
|
-
},
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// Multiple conditions with OR
|
|
86
|
-
const user = await findFirst({
|
|
87
|
-
tableName: 'users',
|
|
88
|
-
dbClient,
|
|
89
|
-
where: {
|
|
90
|
-
OR: [
|
|
91
|
-
{ email: { operator: '=', value: 'user@example.com' } },
|
|
92
|
-
{ phone: { operator: '=', value: '+1234567890' } },
|
|
93
|
-
],
|
|
94
|
-
},
|
|
95
|
-
})
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### With Ordering
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
// Find the most recent user
|
|
102
|
-
const latestUser = await findFirst({
|
|
103
|
-
tableName: 'users',
|
|
104
|
-
dbClient,
|
|
105
|
-
orderBy: [{ field: 'created_at', direction: 'DESC' }],
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
// Find the oldest active user
|
|
109
|
-
const oldestUser = await findFirst({
|
|
110
|
-
tableName: 'users',
|
|
111
|
-
dbClient,
|
|
112
|
-
where: { status: { operator: '=', value: 'active' } },
|
|
113
|
-
orderBy: [{ field: 'created_at', direction: 'ASC' }],
|
|
114
|
-
})
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### With Grouping
|
|
118
|
-
|
|
119
|
-
```typescript
|
|
120
|
-
// Find the first user in each status group
|
|
121
|
-
const userByStatus = await findFirst({
|
|
122
|
-
tableName: 'users',
|
|
123
|
-
dbClient,
|
|
124
|
-
select: ['status', 'name', 'created_at'],
|
|
125
|
-
groupBy: ['status'],
|
|
126
|
-
orderBy: [{ field: 'created_at', direction: 'ASC' }],
|
|
127
|
-
})
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Advanced Conditions
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// Using different operators
|
|
134
|
-
const user = await findFirst({
|
|
135
|
-
tableName: 'users',
|
|
136
|
-
dbClient,
|
|
137
|
-
where: {
|
|
138
|
-
age: { operator: '>=', value: 18 },
|
|
139
|
-
name: { operator: 'LIKE', value: '%John%' },
|
|
140
|
-
email: { operator: 'IS NOT NULL', value: null },
|
|
141
|
-
status: { operator: 'IN', value: ['active', 'pending'] },
|
|
142
|
-
},
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
// Using BETWEEN
|
|
146
|
-
const user = await findFirst({
|
|
147
|
-
tableName: 'users',
|
|
148
|
-
dbClient,
|
|
149
|
-
where: {
|
|
150
|
-
created_at: {
|
|
151
|
-
operator: 'BETWEEN',
|
|
152
|
-
value: [new Date('2023-01-01'), new Date('2023-12-31')],
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
})
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### TypeScript Usage
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
interface User {
|
|
162
|
-
id: string
|
|
163
|
-
name: string
|
|
164
|
-
email: string
|
|
165
|
-
age: number
|
|
166
|
-
status: 'active' | 'inactive' | 'pending'
|
|
167
|
-
created_at: Date
|
|
168
|
-
updated_at: Date
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Typed usage
|
|
172
|
-
const user: User | null = await findFirst<User>({
|
|
173
|
-
tableName: 'users',
|
|
174
|
-
dbClient,
|
|
175
|
-
where: {
|
|
176
|
-
email: { operator: '=', value: 'user@example.com' },
|
|
177
|
-
},
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
if (user) {
|
|
181
|
-
console.log(`Found user: ${user.name}`)
|
|
182
|
-
} else {
|
|
183
|
-
console.log('User not found')
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Error Handling
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
try {
|
|
191
|
-
const user = await findFirst({
|
|
192
|
-
tableName: 'users',
|
|
193
|
-
dbClient,
|
|
194
|
-
where: { email: { operator: '=', value: 'user@example.com' } },
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
if (user) {
|
|
198
|
-
console.log('User found:', user.name)
|
|
199
|
-
} else {
|
|
200
|
-
console.log('No user found with this email')
|
|
201
|
-
}
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error('Database error:', error.message)
|
|
204
|
-
// Handle error appropriately
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
## Generated SQL Examples
|
|
209
|
-
|
|
210
|
-
### Simple Query
|
|
211
|
-
|
|
212
|
-
```sql
|
|
213
|
-
SELECT * FROM users WHERE email = $1 LIMIT 1
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### With Specific Fields
|
|
217
|
-
|
|
218
|
-
```sql
|
|
219
|
-
SELECT id, name, email FROM users WHERE status = $1 LIMIT 1
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### With Complex Conditions
|
|
223
|
-
|
|
224
|
-
```sql
|
|
225
|
-
SELECT * FROM users
|
|
226
|
-
WHERE (email = $1 AND status = $2 AND verified = $3)
|
|
227
|
-
LIMIT 1
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
### With Ordering
|
|
231
|
-
|
|
232
|
-
```sql
|
|
233
|
-
SELECT * FROM users
|
|
234
|
-
ORDER BY created_at DESC
|
|
235
|
-
LIMIT 1
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### With Grouping
|
|
239
|
-
|
|
240
|
-
```sql
|
|
241
|
-
SELECT status, name, created_at
|
|
242
|
-
FROM users
|
|
243
|
-
GROUP BY status
|
|
244
|
-
ORDER BY created_at ASC
|
|
245
|
-
LIMIT 1
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
## Best Practices
|
|
249
|
-
|
|
250
|
-
### 1. Use Specific Field Selection
|
|
251
|
-
|
|
252
|
-
```typescript
|
|
253
|
-
// Good: Select only needed fields
|
|
254
|
-
const user = await findFirst({
|
|
255
|
-
tableName: 'users',
|
|
256
|
-
dbClient,
|
|
257
|
-
select: ['id', 'name', 'email'],
|
|
258
|
-
where: { id: { operator: '=', value: 'user-123' } },
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
// Avoid: Selecting all fields when not needed
|
|
262
|
-
const user = await findFirst({
|
|
263
|
-
tableName: 'users',
|
|
264
|
-
dbClient,
|
|
265
|
-
where: { id: { operator: '=', value: 'user-123' } },
|
|
266
|
-
})
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### 2. Use Appropriate Indexes
|
|
270
|
-
|
|
271
|
-
Ensure your database has indexes on fields used in WHERE clauses for better performance:
|
|
272
|
-
|
|
273
|
-
```sql
|
|
274
|
-
-- Example indexes for common queries
|
|
275
|
-
CREATE INDEX idx_users_email ON users(email);
|
|
276
|
-
CREATE INDEX idx_users_status ON users(status);
|
|
277
|
-
CREATE INDEX idx_users_created_at ON users(created_at);
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### 3. Handle Null Results
|
|
281
|
-
|
|
282
|
-
```typescript
|
|
283
|
-
const user = await findFirst({
|
|
284
|
-
tableName: 'users',
|
|
285
|
-
dbClient,
|
|
286
|
-
where: { email: { operator: '=', value: 'nonexistent@example.com' } },
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
if (!user) {
|
|
290
|
-
// Handle case when no user is found
|
|
291
|
-
throw new Error('User not found')
|
|
292
|
-
}
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
### 4. Use TypeScript for Type Safety
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
interface UserSearchParams {
|
|
299
|
-
email?: string
|
|
300
|
-
status?: string
|
|
301
|
-
age?: number
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const findUserByParams = async (
|
|
305
|
-
params: UserSearchParams
|
|
306
|
-
): Promise<User | null> => {
|
|
307
|
-
const where: Conditions<User> = {}
|
|
308
|
-
|
|
309
|
-
if (params.email) {
|
|
310
|
-
where.email = { operator: '=', value: params.email }
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (params.status) {
|
|
314
|
-
where.status = { operator: '=', value: params.status }
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (params.age) {
|
|
318
|
-
where.age = { operator: '>=', value: params.age }
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return findFirst<User>({
|
|
322
|
-
tableName: 'users',
|
|
323
|
-
dbClient,
|
|
324
|
-
where,
|
|
325
|
-
})
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
## Common Use Cases
|
|
330
|
-
|
|
331
|
-
### 1. User Authentication
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
const authenticateUser = async (email: string, password: string) => {
|
|
335
|
-
const user = await findFirst({
|
|
336
|
-
tableName: 'users',
|
|
337
|
-
dbClient,
|
|
338
|
-
where: {
|
|
339
|
-
AND: [
|
|
340
|
-
{ email: { operator: '=', value: email } },
|
|
341
|
-
{ password: { operator: '=', value: password } },
|
|
342
|
-
{ status: { operator: '=', value: 'active' } },
|
|
343
|
-
],
|
|
344
|
-
},
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
return user
|
|
348
|
-
}
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### 2. Finding Latest Record
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
const getLatestOrder = async (userId: string) => {
|
|
355
|
-
const order = await findFirst({
|
|
356
|
-
tableName: 'orders',
|
|
357
|
-
dbClient,
|
|
358
|
-
where: { user_id: { operator: '=', value: userId } },
|
|
359
|
-
orderBy: [{ field: 'created_at', direction: 'DESC' }],
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
return order
|
|
363
|
-
}
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
### 3. Checking Existence
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
const userExists = async (email: string): Promise<boolean> => {
|
|
370
|
-
const user = await findFirst({
|
|
371
|
-
tableName: 'users',
|
|
372
|
-
dbClient,
|
|
373
|
-
select: ['id'],
|
|
374
|
-
where: { email: { operator: '=', value: email } },
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
return user !== null
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
## Performance Considerations
|
|
382
|
-
|
|
383
|
-
- **Indexes**: Ensure proper indexes exist on fields used in WHERE clauses
|
|
384
|
-
- **Field Selection**: Use `select` to limit returned fields when possible
|
|
385
|
-
- **Limit Results**: `findFirst` automatically limits to 1 result, which is optimal
|
|
386
|
-
- **Connection Pooling**: Use connection pooling for better performance in high-traffic applications
|
|
387
|
-
|
|
388
|
-
## Error Messages
|
|
389
|
-
|
|
390
|
-
Common error messages you might encounter:
|
|
391
|
-
|
|
392
|
-
- `Table name is required` - The `tableName` parameter is missing
|
|
393
|
-
- `DB client is required` - The `dbClient` parameter is missing
|
|
394
|
-
- Database-specific errors from the underlying database driver
|