195 lines
6.4 KiB
Markdown
195 lines
6.4 KiB
Markdown
# Query Helper API Reference
|
|
|
|
This document provides a complete reference for the JSON-based database query helper.
|
|
|
|
## Method Overview
|
|
|
|
| Method | Description | Notes |
|
|
|--------|-------------|-------|
|
|
| `select(options)` | Retrieve records from database | JSON-based query builder |
|
|
| `insert(options)` | Insert new record(s) | Single or batch insert |
|
|
| `update(options)` | Update existing records | Requires WHERE clause |
|
|
| `deleteRecord(options)` | Delete records | Requires WHERE clause |
|
|
| `execute(options)` | Execute transactions or custom queries | Transactions or raw builder |
|
|
|
|
## Detailed Method Reference
|
|
|
|
### SELECT
|
|
|
|
Retrieve records from database with filtering, sorting, and pagination.
|
|
|
|
```javascript
|
|
const users = await select({
|
|
table: 'users',
|
|
where: { deleted: false, status: 'active' },
|
|
orderBy: { column: 'created_at', direction: 'desc' },
|
|
limit: 20
|
|
});
|
|
```
|
|
|
|
**Options:**
|
|
- `table` (string, required): Table name (must be in whitelist)
|
|
- `columns` (string[] | '*', optional): Columns to select (default: '*')
|
|
- `where` (object, optional): WHERE conditions
|
|
- `orderBy` (object, optional): `{ column: string, direction: 'asc'|'desc' }`
|
|
- `limit` (number, optional): Max records (capped at 100)
|
|
- `offset` (number, optional): Skip records
|
|
- `joins` (array, optional): Join configurations
|
|
|
|
### INSERT
|
|
|
|
Insert new record(s) into database.
|
|
|
|
```javascript
|
|
const user = await insert({
|
|
table: 'users',
|
|
data: { name: 'John', phone: '+1234567890' },
|
|
returning: ['id', 'name']
|
|
});
|
|
```
|
|
|
|
**Options:**
|
|
- `table` (string, required): Table name
|
|
- `data` (object | object[], required): Data to insert (single object or array for batch)
|
|
- `returning` (string[] | '*', optional): Columns to return (PostgreSQL)
|
|
|
|
### UPDATE
|
|
|
|
Update existing records in database.
|
|
|
|
```javascript
|
|
const updated = await update({
|
|
table: 'users',
|
|
data: { name: 'Jane' },
|
|
where: { id: userId }
|
|
});
|
|
```
|
|
|
|
**Options:**
|
|
- `table` (string, required): Table name
|
|
- `data` (object, required): Data to update
|
|
- `where` (object, required): WHERE conditions (required for safety)
|
|
- `returning` (string[] | '*', optional): Columns to return
|
|
|
|
### DELETE
|
|
|
|
Delete records from database.
|
|
|
|
```javascript
|
|
await deleteRecord({
|
|
table: 'users',
|
|
where: { id: userId }
|
|
});
|
|
```
|
|
|
|
**Options:**
|
|
- `table` (string, required): Table name
|
|
- `where` (object, required): WHERE conditions (required for safety)
|
|
- `returning` (string[] | '*', optional): Columns to return
|
|
|
|
### EXECUTE (Transaction)
|
|
|
|
Execute transactions or custom query builder logic.
|
|
|
|
```javascript
|
|
await execute({
|
|
type: 'transaction',
|
|
handler: async (trx) => {
|
|
await trx('animals').insert(animalData);
|
|
await trx('listings').insert(listingData);
|
|
}
|
|
});
|
|
```
|
|
|
|
**Options:**
|
|
- `type` (string, required): 'transaction' or 'raw-builder'
|
|
- `handler` (function, required): Handler function receiving knex/trx instance
|
|
|
|
## WHERE Clause Operators
|
|
|
|
| Operator | Description | Example |
|
|
|----------|-------------|---------|
|
|
| Equality | Simple key-value match | `where: { status: 'active' }` |
|
|
| `>` | Greater than | `where: { price: { op: '>', value: 100 } }` |
|
|
| `<` | Less than | `where: { age: { op: '<', value: 18 } }` |
|
|
| `>=` | Greater than or equal | `where: { price: { op: '>=', value: 100 } }` |
|
|
| `<=` | Less than or equal | `where: { age: { op: '<=', value: 65 } }` |
|
|
| `!=` or `<>` | Not equal | `where: { status: { op: '!=', value: 'deleted' } }` |
|
|
| `in` | In array | `where: { id: { op: 'in', value: [1, 2, 3] } }` |
|
|
| `notIn` | Not in array | `where: { id: { op: 'notIn', value: [1, 2, 3] } }` |
|
|
| `like` | Case-sensitive LIKE | `where: { name: { op: 'like', value: '%John%' } }` |
|
|
| `ilike` | Case-insensitive LIKE | `where: { name: { op: 'ilike', value: '%john%' } }` |
|
|
| `between` | Between two values | `where: { age: { op: 'between', value: [18, 65] } }` |
|
|
| `isNull` | IS NULL | `where: { deleted_at: { op: 'isNull' } }` |
|
|
| `isNotNull` | IS NOT NULL | `where: { deleted_at: { op: 'isNotNull' } }` |
|
|
|
|
## Supported Operators
|
|
|
|
| Operator | Description | Example |
|
|
|----------|-------------|---------|
|
|
| `>` | Greater than | `{ op: '>', value: 100 }` |
|
|
| `<` | Less than | `{ op: '<', value: 100 }` |
|
|
| `>=` | Greater than or equal | `{ op: '>=', value: 100 }` |
|
|
| `<=` | Less than or equal | `{ op: '<=', value: 100 }` |
|
|
| `!=` or `<>` | Not equal | `{ op: '!=', value: 'deleted' }` |
|
|
| `in` | In array | `{ op: 'in', value: [1, 2, 3] }` |
|
|
| `notIn` | Not in array | `{ op: 'notIn', value: [1, 2, 3] }` |
|
|
| `like` | Case-sensitive LIKE | `{ op: 'like', value: '%test%' }` |
|
|
| `ilike` | Case-insensitive LIKE | `{ op: 'ilike', value: '%test%' }` |
|
|
| `between` | Between two values | `{ op: 'between', value: [10, 20] }` |
|
|
| `isNull` | IS NULL | `{ op: 'isNull' }` |
|
|
| `isNotNull` | IS NOT NULL | `{ op: 'isNotNull' }` |
|
|
|
|
## Naming Conventions
|
|
|
|
- **Table names**: lowercase, snake_case (e.g., `users`, `listing_media`)
|
|
- **Column names**: lowercase, snake_case (e.g., `created_at`, `user_id`)
|
|
- **Options**: camelCase (e.g., `orderBy`, `returning`)
|
|
- **Operators**: lowercase (e.g., `op: '>'`)
|
|
|
|
## Type System
|
|
|
|
- **JavaScript**: JSON objects with runtime validation
|
|
- **Runtime Validation**: Descriptive errors for invalid inputs
|
|
- **Type Safety**: Structured JSON prevents SQL injection
|
|
|
|
## Error Handling
|
|
|
|
- **Runtime Validation**: All inputs validated at runtime
|
|
- **Descriptive Errors**: Clear error messages for debugging
|
|
- **Database Errors**: Propagated with context
|
|
|
|
## Transactions
|
|
|
|
```javascript
|
|
await execute({
|
|
type: 'transaction',
|
|
handler: async (trx) => {
|
|
await trx('users').insert(userData);
|
|
await trx('listings').insert(listingData);
|
|
}
|
|
});
|
|
```
|
|
|
|
## Migration Checklist
|
|
|
|
When migrating from raw SQL to queryHelper:
|
|
|
|
- [ ] Replace `pool.query()` with `select()`, `insert()`, `update()`, or `deleteRecord()`
|
|
- [ ] Convert SQL WHERE clauses to JSON objects
|
|
- [ ] Replace string concatenation with structured options
|
|
- [ ] Convert transactions to `execute({ type: 'transaction' })`
|
|
- [ ] Update JOINs to use `joins` array
|
|
- [ ] Replace parameterized queries with JSON where conditions
|
|
- [ ] Test all queries to ensure same results
|
|
- [ ] Remove all raw SQL strings from service layer
|
|
|
|
## Benefits
|
|
|
|
1. **Type Safety**: Structured JSON prevents SQL injection
|
|
2. **Readability**: Clear, self-documenting queries
|
|
3. **Maintainability**: Easy to modify and extend
|
|
4. **Security**: Table whitelist prevents unauthorized access
|
|
5. **Consistency**: Uniform query interface across codebase
|
|
6. **Testing**: Easier to mock and test
|