# Query Helper - JSON-Based Database Interface A centralized, SQL-string-free database query helper built on Knex.js that provides a safe, structured interface for all database operations. ## 🎯 Goals - **No Raw SQL**: All queries built using Knex query builder - **JSON-Based**: Structured JSON objects instead of SQL strings - **Type Safe**: Parameterized queries prevent SQL injection - **Server-Only**: Internal helper, not exposed to API clients - **Production Ready**: Error handling, validation, and security built-in ## 📦 Installation Knex.js is already installed. The query helper uses the existing database connection from `db/pool.js`. ## 🚀 Quick Start ```javascript import { select, insert, update, deleteRecord, execute } from '../db/queryHelper/index.js'; // SELECT const users = await select({ table: 'users', where: { deleted: false }, limit: 10 }); // INSERT const user = await insert({ table: 'users', data: { name: 'John', phone_number: '+1234567890' }, returning: '*' }); // UPDATE const updated = await update({ table: 'users', data: { name: 'Jane' }, where: { id: userId }, returning: '*' }); // DELETE await deleteRecord({ table: 'users', where: { id: userId } }); // TRANSACTION await execute({ type: 'transaction', handler: async (trx) => { await trx('users').insert(userData); await trx('listings').insert(listingData); } }); ``` ## 📚 API Reference ### `select(options)` Retrieve records from database. **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 **Returns:** `Promise` ### `insert(options)` Insert new record(s). **Options:** - `table` (string, required): Table name - `data` (object | object[], required): Data to insert - `returning` (string[] | '*', optional): Columns to return **Returns:** `Promise` ### `update(options)` Update existing records. **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 **Returns:** `Promise` ### `deleteRecord(options)` Delete records. **Options:** - `table` (string, required): Table name - `where` (object, required): WHERE conditions (required for safety) - `returning` (string[] | '*', optional): Columns to return **Returns:** `Promise` ### `execute(options)` Execute transactions or custom query builder logic. **Options:** - `type` (string, required): 'transaction' or 'raw-builder' - `handler` (function, required): Handler function receiving knex/trx instance **Returns:** `Promise` ## 🔍 WHERE Clause Operators ### Simple Equality ```javascript where: { status: 'active' } // WHERE status = 'active' ``` ### Comparison Operators ```javascript where: { price: { op: '>=', value: 1000 }, age: { op: '<', value: 65 } } // WHERE price >= 1000 AND age < 65 ``` ### IN Operator ```javascript where: { id: { op: 'in', value: ['uuid1', 'uuid2', 'uuid3'] } } // WHERE id IN ('uuid1', 'uuid2', 'uuid3') ``` ### LIKE Operator ```javascript where: { name: { op: 'ilike', value: '%john%' } } // WHERE name ILIKE '%john%' ``` ### BETWEEN Operator ```javascript where: { age: { op: 'between', value: [18, 65] } } // WHERE age BETWEEN 18 AND 65 ``` ### NULL Checks ```javascript where: { deleted_at: { op: 'isNull' } } // WHERE deleted_at IS NULL ``` ## 🔒 Security Features 1. **Table Whitelist**: Only allowed tables can be queried 2. **Parameterized Queries**: All values are parameterized 3. **WHERE Required**: UPDATE/DELETE require WHERE clause 4. **Limit Caps**: Maximum 100 records per query 5. **No SQL Strings**: Impossible to inject SQL ## 📋 Allowed Tables The following tables are whitelisted: - `users` - `listings` - `animals` - `locations` - `species` - `breeds` - `listing_media` - `conversations` - `messages` - `communication_records` - `favorites` - `user_devices` - `subscription_plans` - `otp_requests` - `auth_audit` To add more tables, update `ALLOWED_TABLES` in `queryHelper.js`. ## 🎓 Examples See `db/queryHelper/examples.js` for comprehensive examples. ## 📖 Migration Guide See `db/MIGRATION_EXAMPLE.md` for before/after comparisons. ## 🔄 API Reference See `db/QUERY_HELPER_MAPPING.md` for complete method reference and examples. ## ⚠️ Important Notes 1. **No Raw SQL**: Never use `pool.query()` with SQL strings 2. **Always Use WHERE**: UPDATE/DELETE must include WHERE clause 3. **Table Names**: Must be in whitelist (case-sensitive) 4. **Transactions**: Use `execute({ type: 'transaction' })` for multi-step operations 5. **Complex Queries**: Use `execute({ type: 'raw-builder' })` for advanced cases ## 🐛 Error Handling The query helper throws descriptive errors: - `Table 'xxx' is not allowed` - Table not in whitelist - `WHERE clause is required` - Missing WHERE for UPDATE/DELETE - `Unsupported operator: xxx` - Invalid operator - Database errors are propagated with context ## 📝 Best Practices 1. **Use Transactions**: For multi-step operations 2. **Soft Deletes**: Use UPDATE instead of DELETE 3. **Limit Results**: Always set reasonable limits 4. **Validate Input**: Validate data before passing to helper 5. **Handle Errors**: Wrap in try-catch blocks 6. **Use Returning**: Get inserted/updated records back ## 🔧 Advanced Usage ### Complex Joins ```javascript const results = await select({ table: 'listings', columns: ['listings.*', 'animals.*'], joins: [ { type: 'inner', table: 'animals', on: 'listings.animal_id = animals.id' } ], where: { 'listings.deleted': false } }); ``` ### Custom Query Builder ```javascript const results = await execute({ type: 'raw-builder', handler: async (knex) => { return await knex('listings') .select('listings.*') .join('animals', 'listings.animal_id', 'animals.id') .where('listings.deleted', false) .where('animals.species_id', speciesId) .orderBy('listings.created_at', 'desc') .limit(20); } }); ``` ## 📚 Documentation - **DEVELOPER_GUIDE.md** - Complete developer guide with examples, flow diagrams, and best practices - **QUICK_REFERENCE.md** - One-page quick reference card for daily use - **API_REFERENCE.md** - Complete API reference with all methods and options ## 📞 Support For questions or issues, refer to: - `DEVELOPER_GUIDE.md` - Comprehensive guide with real-world examples from the codebase - `QUICK_REFERENCE.md` - Quick lookup for common patterns - `API_REFERENCE.md` - Complete method documentation