@trithanka/sql-builder
Version:
A lightweight, function-based, chainable SQL query builder for Node.js using MySQL pool connections.
323 lines (254 loc) โข 8.61 kB
Markdown
# SQL Query Builder ๐งฉ
[](https://www.npmjs.com/package/@trithanka/sql-builder)
[](https://www.npmjs.com/package/@trithanka/sql-builder)
[](https://github.com/Trithanka/sql-query-builder/blob/main/LICENSE)
[](https://github.com/Trithanka/sql-query-builder/actions)
A lightweight, function-based, chainable SQL query builder for Node.js projects using MySQL/MariaDB pool connections and raw queries. Perfect for building dynamic filters, pagination, and safe SQL operations โ without a full-blown ORM.
---
## ๐ฆ Features
- โ
Function-based & chainable API
- โ
Safe parameterized queries (`?` bindings)
- โ
Works seamlessly with MySQL `pool.execute` / `pool.query`
- โ
Supports: `SELECT`, `INSERT`, `UPDATE`, `DELETE`
- โ
Clean dynamic filter generation
- โ
Pagination & ordering support
- โ
**Grouping** (`.groupBy(...)`) & **HAVING** (`.having(...)`)
- โ
**Total-count** in one call (`.build("count")`)
- โ
**SQL Injection Protection** with input validation
- โ
**Smart WHERE clause detection** (handles existing WHERE clauses)
- โ
**Comprehensive error handling** with clear messages
---
## ๐ง Installation
```bash
npm install @trithanka/sql-builder
```
---
## ๐ Usage
### ๐ SELECT Examples
#### Basic SELECT with Filters (Without Count)
```js
const { createSelectBuilder } = require('@trithanka/sql-builder');
const { sql, values } = createSelectBuilder('SELECT * FROM users')
.where('status = ?', 'active')
.where('age >= ?', 18)
.where('created_at >= ?', '2024-01-01')
.orderBy('created_at', 'DESC')
.paginate(10, 0)
.build();
console.log('SQL:', sql);
console.log('Values:', values);
// Output:
// SQL: SELECT * FROM users WHERE status = ? AND age >= ? AND created_at >= ? ORDER BY created_at DESC LIMIT ? OFFSET ?
// Values: ['active', 18, '2024-01-01', 10, 0]
const [rows] = await pool.execute(sql, values);
```
#### Advanced SELECT with Grouping and Count
```js
const { createSelectBuilder } = require('@trithanka/sql-builder');
const {
sql,
values,
countSql,
countValues
} = createSelectBuilder(`
SELECT seller_id, COUNT(*) AS sales_count, SUM(amount) AS total_sales
FROM orders
`)
.where('order_date >= ?', '2024-01-01')
.where('status = ?', 'completed')
.groupBy('seller_id')
.having('COUNT(*) >= ?', 5)
.having('SUM(amount) >= ?', 1000)
.orderBy('total_sales', 'DESC')
.paginate(20, 40)
.build('count');
console.log('Main SQL:', sql);
console.log('Main Values:', values);
console.log('Count SQL:', countSql);
console.log('Count Values:', countValues);
// Get paginated results
const [rows] = await pool.execute(sql, values);
// Get total count for pagination
const [[{ total }]] = await pool.execute(countSql, countValues);
console.log('Total records:', total);
```
#### SELECT with Existing WHERE Clause
```js
const { createSelectBuilder } = require('@trithanka/sql-builder');
// Base SQL already has WHERE clause
const { sql, values } = createSelectBuilder(`
SELECT * FROM users
WHERE status = 'active' AND role = 'admin'
`)
.where('age > ?', 25)
.where('department = ?', 'IT')
.orderBy('name', 'ASC')
.build();
console.log('SQL:', sql);
// Output: SELECT * FROM users WHERE status = 'active' AND role = 'admin' AND age > ? AND department = ? ORDER BY name ASC
```
#### Complex SELECT with Multiple Conditions
```js
const { createSelectBuilder } = require('@trithanka/sql-builder');
const filters = {
status: 'active',
role: 'user',
fromDate: '2024-01-01',
toDate: '2024-12-31',
minAge: 18,
maxAge: 65,
department: 'engineering',
limit: 50,
offset: 0
};
const { sql, values, countSql, countValues } = createSelectBuilder(`
SELECT u.id, u.name, u.email, u.created_at,
COUNT(o.id) as order_count, SUM(o.amount) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
`)
.where('u.status = ?', filters.status)
.where('u.role = ?', filters.role)
.where('u.age >= ?', filters.minAge)
.where('u.age <= ?', filters.maxAge)
.where('u.department = ?', filters.department)
.where('u.created_at >= ?', filters.fromDate)
.where('u.created_at <= ?', filters.toDate)
.groupBy('u.id, u.name, u.email, u.created_at')
.having('COUNT(o.id) > ?', 0)
.orderBy('total_spent', 'DESC')
.paginate(filters.limit, filters.offset)
.build('count');
// Execute both queries
const [rows] = await pool.execute(sql, values);
const [[{ total }]] = await pool.execute(countSql, countValues);
console.log(`Found ${rows.length} users out of ${total} total`);
```
### ๐ INSERT Examples
```js
const { buildInsertQuery } = require('@trithanka/sql-builder');
// Single record insert
const { sql, values } = buildInsertQuery('users', {
name: 'John Doe',
email: 'john@example.com',
age: 30,
status: 'active'
});
console.log('SQL:', sql);
console.log('Values:', values);
// Output:
// SQL: INSERT INTO users (name, email, age, status) VALUES (?, ?, ?, ?)
// Values: ['John Doe', 'john@example.com', 30, 'active']
await pool.execute(sql, values);
```
### โ๏ธ UPDATE Examples
```js
const { buildUpdateQuery } = require('@trithanka/sql-builder');
// Update with single condition
const { sql, values } = buildUpdateQuery(
'users',
{
name: 'John Smith',
email: 'johnsmith@example.com',
updated_at: new Date().toISOString()
},
'id = ?',
[101]
);
console.log('SQL:', sql);
console.log('Values:', values);
// Output:
// SQL: UPDATE users SET name = ?, email = ?, updated_at = ? WHERE id = ?
// Values: ['John Smith', 'johnsmith@example.com', '2024-01-15T10:30:00.000Z', 101]
await pool.execute(sql, values);
```
### โ DELETE Examples
```js
const { buildDeleteQuery } = require('@trithanka/sql-builder');
// Delete with single condition
const { sql, values } = buildDeleteQuery('users', 'id = ?', [101]);
console.log('SQL:', sql);
console.log('Values:', values);
// Output:
// SQL: DELETE FROM users WHERE id = ?
// Values: [101]
await pool.execute(sql, values);
```
---
## ๐ก๏ธ Security Features
### SQL Injection Protection
```js
// โ This will throw an error (SQL injection attempt)
try {
createSelectBuilder('SELECT * FROM users')
.orderBy('id; DROP TABLE users; --', 'ASC')
.build();
} catch (error) {
console.log('SQL injection prevented:', error.message);
}
// โ
This works safely
createSelectBuilder('SELECT * FROM users')
.orderBy('id', 'ASC')
.build();
```
### Input Validation
```js
// โ Invalid pagination values
try {
createSelectBuilder('SELECT * FROM users')
.paginate(-5, -10)
.build();
} catch (error) {
console.log('Invalid pagination rejected:', error.message);
}
// โ Invalid column names
try {
createSelectBuilder('SELECT * FROM users')
.orderBy('invalid;column;name', 'ASC')
.build();
} catch (error) {
console.log('Invalid column name rejected:', error.message);
}
```
### Smart WHERE Detection
```js
// Handles existing WHERE clauses correctly
const { sql } = createSelectBuilder(`
SELECT * FROM users
WHERE status = 'active' -- This comment won't interfere
AND role = 'admin'
`)
.where('age > ?', 25)
.build();
console.log('SQL:', sql);
// Output: SELECT * FROM users WHERE status = 'active' AND role = 'admin' AND age > ?
```
---
## ๐ Folder Structure
```bash
src/
โโโ selectBuilder.js # SELECT query builder with count support
โโโ insertBuilder.js # INSERT query builder
โโโ updateBuilder.js # UPDATE query builder
โโโ deleteBuilder.js # DELETE query builder
โโโ index.js # Main exports
```
---
## ๐ง When Should You Use This?
- When you're using raw SQL (`pool.execute`) and need reusable filters
- When you want full control without the overhead of an ORM like Sequelize
- When building admin panels, dashboards, reports, or public APIs
- When writing secure SQL using parameter binding
- When you need both data and count queries for pagination
- When you want comprehensive input validation and SQL injection protection
---
## ๐งช Coming Soon
- `.whereIn(field, [...values])`
- `.between(field, from, to)`
- `.like(field, pattern)`
- `.isNull(field)` / `.isNotNull(field)`
- TypeScript support
- Support for other databases (PostgreSQL, SQLite)
---
## ๐ License
MIT ยฉ Trithanka