@dbcube/query-builder
Version:
The Dbcube Query Builder is a lightweight, flexible, and fluent library for building queries across multiple database engines, including MySQL, PostgreSQL, SQLite, and MongoDB, using JavaScript/Node.js. Its agnostic design allows you to generate data man
1,700 lines (1,273 loc) • 46 kB
Markdown
# /query-builder
## Language/Lenguaje
- [English](#english-documentation)
- [Español](#documentación-en-español)
---
## English documentation
## Table of contents 🚀
- [Introduction](#introduction)
- [Features](#features)
- [Installation](#installation)
- [Configuration](#configuration)
- [Basic Usage](#basic-usage)
- [Database Connection](#database-connection)
- [Table Operations](#table-operations)
- [CRUD Operations](#crud-operations)
- [Inserting Data](#inserting-data)
- [Selecting Data](#selecting-data)
- [Updating Data](#updating-data)
- [Deleting Data](#deleting-data)
- [Advanced Queries](#advanced-queries)
- [WHERE Query](#where-query)
- [OR WHERE Query](#or-where-query)
- [WHERE Condition Groups](#where-condition-groups)
- [BETWEEN Query](#between-query)
- [IN Query](#in-query)
- [IS NULL / IS NOT NULL Query](#is-null--is-not-null-query)
- [JOIN Query](#join-query)
- [LEFT JOIN Query](#left-join-query)
- [RIGHT JOIN Query](#right-join-query)
- [ORDER BY Query](#order-by-query)
- [LIMIT and OFFSET Query (Pagination)](#limit-and-offset-query-pagination)
- [GROUP BY Query](#group-by-query)
- [DISTINCT Query](#distinct-query)
- [Aggregate Functions](#aggregate-functions)
- [count](#count)
- [sum](#sum)
- [avg](#avg)
- [max](#max)
- [min](#min)
- [Finding Records](#finding-records)
- [find](#find)
- [first](#first)
- [Computed Fields and Triggers](#computed-fields-and-triggers)
- [Executing Raw SQL Queries](#executing-raw-sql-queries)
- [Error Handling](#error-handling)
- [Complete API](#complete-api)
- [Database Class](#database-class)
- [Table Class](#table-class)
- [Multi-Database Support](#multi-database-support)
- [Advanced Features](#advanced-features)
- [License](#license)
## Introduction
`/query-builder` is a lightweight, flexible, and fluent Node.js library designed to build queries across multiple database engines, including MySQL, PostgreSQL, SQLite, and MongoDB, using JavaScript/TypeScript.
Its agnostic design allows you to generate data manipulation (DML) and data definition (DDL) operations with a clean, chainable syntax—without sacrificing power or expressiveness. It's designed to work seamlessly in both SQL and NoSQL environments, providing a consistent abstraction layer across different storage technologies while still leveraging the native capabilities of each engine.
**Key differentiator**: Unlike other query builders that focus on a single database type, Dbcube Query Builder provides a unified API that works across SQL and NoSQL databases, making it perfect for modern polyglot architectures.
## Features
- **Multi-Database Support**: MySQL, PostgreSQL, SQLite, and MongoDB
- **Fluent API** for building SQL queries with chainable methods
- **Type-safe** query construction with TypeScript support
- **Complete CRUD Operations**: SELECT, INSERT, UPDATE, DELETE
- **Advanced WHERE conditions** (AND, OR, groups, BETWEEN, IN, NULL checks)
- **JOIN Support**: INNER, LEFT, RIGHT joins
- **Aggregations**: COUNT, SUM, AVG, MAX, MIN
- **Query Modifiers**: ORDER BY, GROUP BY, DISTINCT, LIMIT/OFFSET
- **Computed Fields**: Dynamic field calculations
- **Triggers**: Before/after operation hooks
- **Promise-based asynchronous API**
- **Connection pooling** through /core
- **Error handling** with descriptive messages
- **Cross-platform compatibility** (Windows, macOS, Linux)
## Installation
```bash
npm install /query-builder
```
## Configuration
Dbcube Query Builder works with the Dbcube ecosystem. Make sure you have the proper database configuration through /core.
```typescript
// No explicit configuration needed - works through Dbcube core
import { Database } from "@dbcube/query-builder";
```
## Basic Usage
### Database Connection
The connection is automatically managed through the Dbcube core system.
```typescript
import { Database } from "@dbcube/query-builder";
// Create a database instance
const db = new Database("my_database");
// Get a table reference
const usersTable = db.table("users");
```
## Table Operations
Dbcube Query Builder focuses on data operations. Table creation and schema management are handled by other Dbcube components.
```typescript
// Access table for operations
const usersTable = db.table("users");
```
## CRUD Operations
### Inserting Data
Use the `insert` method to add new records to a table.
```typescript
// Insert single record
const newUser = await usersTable.insert([
{ name: "Alice", email: "alice@example.com", age: 28 }
]);
// Insert multiple records
const newUsers = await usersTable.insert([
{ name: "Alice", email: "alice@example.com", age: 28 },
{ name: "Bob", email: "bob@example.com", age: 32 },
{ name: "Charlie", email: "charlie@example.com", age: 35 }
]);
console.log(newUsers);
// Returns the inserted records with generated IDs
```
### Selecting Data
Use the `select` method to specify columns and `get()` to retrieve data.
```typescript
// Select all columns
const allUsers = await usersTable.get();
// Select specific columns
const users = await usersTable
.select(["id", "name", "email"])
.get();
// Select with conditions
const activeUsers = await usersTable
.select(["name", "email"])
.where("status", "=", "active")
.orderBy("created_at", "DESC")
.limit(10)
.get();
console.log(users);
// [{ id: 1, name: 'Alice', email: 'alice@example.com' }, ...]
```
### Updating Data
Use the `update` method to modify existing records. **Requires WHERE conditions**.
```typescript
// Update single field
await usersTable
.where("id", "=", 1)
.update({ age: 29 });
// Update multiple fields
await usersTable
.where("email", "=", "alice@example.com")
.update({
name: "Alice Smith",
age: 29,
updated_at: new Date()
});
// Update with complex conditions
await usersTable
.where("age", ">", 30)
.where("status", "=", "inactive")
.update({ status: "archived" });
```
### Deleting Data
Use the `delete` method to remove records. **Requires WHERE conditions**.
```typescript
// Delete specific record
await usersTable
.where("id", "=", 2)
.delete();
// Delete with conditions
await usersTable
.where("status", "=", "deleted")
.where("created_at", "<", "2023-01-01")
.delete();
```
## Advanced Queries
### WHERE Query
Filter records using the `where` method with various operators.
```typescript
// Basic comparisons
const adultUsers = await usersTable.where("age", ">", 18).get();
const exactMatch = await usersTable.where("name", "=", "Alice").get();
const notEqual = await usersTable.where("status", "!=", "deleted").get();
// String operations
const emailUsers = await usersTable.where("email", "LIKE", "%@gmail.com").get();
console.log(adultUsers);
// [{ id: 1, name: 'Alice', age: 28 }, ...]
```
### OR WHERE Query
Use `orWhere` to add OR conditions to your query.
```typescript
const users = await usersTable
.where("age", ">", 25)
.orWhere("name", "=", "Alice")
.get();
// Complex OR conditions
const premiumUsers = await usersTable
.where("subscription", "=", "premium")
.orWhere("total_purchases", ">", 1000)
.orWhere("member_since", "<", "2020-01-01")
.get();
console.log(users);
// [{ id: 1, name: 'Alice', age: 28 }, ...]
```
### WHERE Condition Groups
Group conditions using `whereGroup` for complex logic.
```typescript
// (age > 25 OR name = 'Jane') AND status = 'active'
const users = await usersTable
.whereGroup((query) => {
query.where("age", ">", 25).orWhere("name", "=", "Jane");
})
.where("status", "=", "active")
.get();
// Nested groups
const complexQuery = await usersTable
.whereGroup((query) => {
query.where("country", "=", "US").orWhere("country", "=", "CA");
})
.where("active", "=", true)
.whereGroup((query) => {
query.where("age", ">=", 21).orWhere("verified", "=", true);
})
.get();
```
### BETWEEN Query
Search for values within a range using `whereBetween`.
```typescript
// Age between 25 and 35
const users = await usersTable.whereBetween("age", [25, 35]).get();
// Date ranges
const recentUsers = await usersTable
.whereBetween("created_at", ["2024-01-01", "2024-12-31"])
.get();
console.log(users);
// [{ id: 1, name: 'Alice', age: 28 }, { id: 2, name: 'Bob', age: 32 }]
```
### IN Query
Search for values that match a set of values using `whereIn`.
```typescript
// Specific IDs
const users = await usersTable.whereIn("id", [1, 3, 5]).get();
// Multiple statuses
const filteredUsers = await usersTable
.whereIn("status", ["active", "pending", "verified"])
.get();
// String values
const emailDomains = await usersTable
.whereIn("email_domain", ["gmail.com", "yahoo.com", "hotmail.com"])
.get();
console.log(users);
// [{ id: 1, name: 'Alice', age: 28 }, { id: 3, name: 'Charlie', age: 35 }]
```
### IS NULL / IS NOT NULL Query
Search for null or non-null values using `whereNull` and `whereNotNull`.
```typescript
// Users without email
const usersWithoutEmail = await usersTable.whereNull("email").get();
// Users with email
const usersWithEmail = await usersTable.whereNotNull("email").get();
// Combine with other conditions
const incompleteProfiles = await usersTable
.whereNull("phone")
.orWhere("avatar", "IS NULL")
.whereNotNull("email")
.get();
```
### JOIN Query
Join tables using the `join` method for INNER JOIN.
```typescript
// Basic INNER JOIN
const usersWithOrders = await usersTable
.join("orders", "users.id", "=", "orders.user_id")
.select(["users.name", "orders.order_id", "orders.total"])
.get();
// Multiple JOINs
const detailedOrders = await usersTable
.join("orders", "users.id", "=", "orders.user_id")
.join("order_items", "orders.id", "=", "order_items.order_id")
.join("products", "order_items.product_id", "=", "products.id")
.select(["users.name", "orders.order_id", "products.name AS product_name"])
.get();
console.log(usersWithOrders);
// [{ name: 'Alice', order_id: 101, total: 150.00 }, ...]
```
### LEFT JOIN Query
Perform a left join using the `leftJoin` method.
```typescript
// Include users even if they have no orders
const usersWithOrders = await usersTable
.leftJoin("orders", "users.id", "=", "orders.user_id")
.select(["users.name", "orders.order_id"])
.get();
// Left join with aggregation
const usersOrderCount = await usersTable
.leftJoin("orders", "users.id", "=", "orders.user_id")
.select(["users.name"])
.count("orders.id")
.groupBy("users.id")
.get();
console.log(usersWithOrders);
// [{ name: 'Alice', order_id: 101 }, { name: 'Bob', order_id: null }, ...]
```
### RIGHT JOIN Query
Perform a right join using the `rightJoin` method.
```typescript
// Include orders even if user data is missing
const ordersWithUsers = await usersTable
.rightJoin("orders", "users.id", "=", "orders.user_id")
.select(["users.name", "orders.order_id"])
.get();
console.log(ordersWithUsers);
// [{ name: 'Alice', order_id: 101 }, { name: null, order_id: 102 }, ...]
```
### ORDER BY Query
Sort results using the `orderBy` method.
```typescript
// Single column sorting
const sortedUsers = await usersTable
.orderBy("name", "ASC")
.get();
// Multiple column sorting
const complexSort = await usersTable
.orderBy("country", "ASC")
.orderBy("age", "DESC")
.orderBy("name", "ASC")
.get();
// Sort with conditions
const recentActiveUsers = await usersTable
.where("status", "=", "active")
.orderBy("last_login", "DESC")
.limit(20)
.get();
console.log(sortedUsers);
// [{ id: 1, name: 'Alice', ... }, { id: 2, name: 'Bob', ... }]
```
### LIMIT and OFFSET Query (Pagination)
Limit the number of results and implement pagination using `limit` and `page`.
```typescript
// Simple limit
const firstTenUsers = await usersTable.limit(10).get();
// Pagination
const firstPage = await usersTable.limit(5).page(1).get();
const secondPage = await usersTable.limit(5).page(2).get();
// Pagination with sorting
const paginatedUsers = await usersTable
.orderBy("created_at", "DESC")
.limit(10)
.page(3) // Skip first 20 records (pages 1-2)
.get();
console.log(firstPage); // Records 1-5
console.log(secondPage); // Records 6-10
```
### GROUP BY Query
Group results using the `groupBy` method.
```typescript
// Simple grouping
const usersByAge = await usersTable
.select(["age"])
.count("*")
.groupBy("age")
.get();
// Multiple grouping columns
const usersByCountryAndAge = await usersTable
.select(["country", "age"])
.count("*")
.groupBy("country")
.groupBy("age")
.get();
console.log(usersByAge);
// [{ age: 28, count: 2 }, { age: 32, count: 1 }]
```
### DISTINCT Query
Retrieve unique records using the `distinct` method.
```typescript
// Distinct values
const uniqueCountries = await usersTable
.distinct()
.select(["country"])
.get();
// Distinct with conditions
const activeUserCountries = await usersTable
.distinct()
.select(["country"])
.where("status", "=", "active")
.get();
console.log(uniqueCountries);
// [{ country: 'USA' }, { country: 'Canada' }, { country: 'UK' }]
```
## Aggregate Functions
### count
Count the number of records.
```typescript
// Count all records
const totalUsers = await usersTable.count().first();
console.log(totalUsers); // { count: 150 }
// Count specific column
const usersWithEmail = await usersTable.count("email").first();
// Count with conditions
const activeUsers = await usersTable
.where("status", "=", "active")
.count()
.first();
```
### sum
Calculate the sum of a column.
```typescript
// Sum of ages
const totalAge = await usersTable.sum("age").first();
console.log(totalAge); // { sum: 4250 }
// Sum with conditions
const premiumRevenue = await usersTable
.join("orders", "users.id", "=", "orders.user_id")
.where("users.subscription", "=", "premium")
.sum("orders.total")
.first();
```
### avg
Calculate the average of a column.
```typescript
// Average age
const averageAge = await usersTable.avg("age").first();
console.log(averageAge); // { avg: 28.33 }
// Average with grouping
const avgAgeByCountry = await usersTable
.select(["country"])
.avg("age")
.groupBy("country")
.get();
```
### max
Find the maximum value in a column.
```typescript
// Oldest user
const maxAge = await usersTable.max("age").first();
console.log(maxAge); // { max: 65 }
// Most recent registration
const latestUser = await usersTable.max("created_at").first();
```
### min
Find the minimum value in a column.
```typescript
// Youngest user
const minAge = await usersTable.min("age").first();
console.log(minAge); // { min: 18 }
// Earliest registration
const firstUser = await usersTable.min("created_at").first();
```
## Finding Records
### find
Find a record by a specific column value (defaults to 'id').
```typescript
// Find by ID (default)
const user = await usersTable.find(1);
console.log(user);
// { id: 1, name: 'Alice', email: 'alice@example.com', age: 28 }
// Find by specific column
const userByEmail = await usersTable.find("alice@example.com", "email");
// Find returns null if not found
const nonExistent = await usersTable.find(999);
console.log(nonExistent); // null
```
### first
Get only the first record that meets the conditions.
```typescript
// First user matching condition
const firstUser = await usersTable
.where("age", ">", 25)
.orderBy("created_at", "ASC")
.first();
// First user in general
const oldestAccount = await usersTable
.orderBy("created_at", "ASC")
.first();
console.log(firstUser);
// { id: 1, name: 'Alice', age: 28, ... } or null if no match
```
## Computed Fields and Triggers
Dbcube Query Builder supports computed fields and triggers for advanced data processing.
```typescript
// Enable computed fields (processed automatically)
await db.useComputes();
// Enable triggers
await db.useTriggers();
// Triggers and computed fields are configured through other Dbcube components
```
## Executing Raw SQL Queries
For complex queries that require raw SQL, use the underlying engine.
```typescript
// Access the underlying engine for raw queries
// (Implementation depends on your specific Dbcube core setup)
// Note: Raw SQL queries bypass the query builder's abstraction layer
// and are database-specific
```
### Error Handling
The library provides comprehensive error handling with descriptive messages.
```typescript
try {
// This will throw an error - UPDATE requires WHERE conditions
await usersTable.update({ name: "Updated" });
} catch (error) {
console.error(error.message);
// "You must specify at least one WHERE condition to perform an update."
}
try {
// This will throw an error - invalid data format
await usersTable.insert("invalid data");
} catch (error) {
console.error(error.message);
// "The insert method requires an array of objects with key-value pairs."
}
// Connection and database errors are also properly handled
try {
const result = await usersTable.get();
} catch (error) {
// Database connection or query execution errors
console.error("Database error:", error);
}
```
## Complete API
### Database Class
#### `new Database(name: string)`
Creates a new database connection instance.
```typescript
const db = new Database("my_database");
```
#### `table(tableName: string): Table`
Returns a Table instance for building queries on the specified table.
```typescript
const usersTable = db.table("users");
```
#### `useComputes(): Promise<void>`
Enables computed fields processing for the database.
```typescript
await db.useComputes();
```
#### `useTriggers(): Promise<void>`
Enables trigger processing for the database.
```typescript
await db.useTriggers();
```
#### `connect(): Promise<void>`
Establishes database connection (handled automatically).
#### `disconnect(): Promise<void>`
Closes database connection.
### Table Class
#### Query Building Methods
**`select(fields?: string[]): Table`**
Specify columns to select.
```typescript
usersTable.select(["id", "name", "email"]);
```
**`where(column, operator, value): Table`**
Add a WHERE condition.
```typescript
usersTable.where("age", ">", 25);
usersTable.where("email", "IS NOT NULL");
```
**`orWhere(column, operator, value): Table`**
Add an OR WHERE condition.
```typescript
usersTable.orWhere("name", "=", "Jane");
```
**`whereGroup(callback): Table`**
Grouped WHERE conditions.
```typescript
usersTable.whereGroup((query) => {
query.where("age", ">", 25).orWhere("name", "=", "Jane");
});
```
**`whereBetween(column, [min, max]): Table`**
WHERE BETWEEN condition.
```typescript
usersTable.whereBetween("age", [25, 35]);
```
**`whereIn(column, values): Table`**
WHERE IN condition.
```typescript
usersTable.whereIn("id", [1, 3, 5]);
```
**`whereNull(column): Table`** / **`whereNotNull(column): Table`**
NULL checks.
```typescript
usersTable.whereNull("deleted_at");
usersTable.whereNotNull("email");
```
**`join(table, column1, operator, column2): Table`**
INNER JOIN.
```typescript
usersTable.join("orders", "users.id", "=", "orders.user_id");
```
**`leftJoin(table, column1, operator, column2): Table`**
LEFT JOIN.
**`rightJoin(table, column1, operator, column2): Table`**
RIGHT JOIN.
**`orderBy(column, direction): Table`**
ORDER BY clause.
```typescript
usersTable.orderBy("name", "ASC");
```
**`groupBy(column): Table`**
GROUP BY clause.
**`distinct(): Table`**
DISTINCT clause.
**`limit(number): Table`**
LIMIT clause.
**`page(number): Table`**
Pagination (requires limit).
#### Aggregation Methods
**`count(column?): Table`**
COUNT aggregation.
**`sum(column): Table`**
SUM aggregation.
**`avg(column): Table`**
AVG aggregation.
**`max(column): Table`**
MAX aggregation.
**`min(column): Table`**
MIN aggregation.
#### Execution Methods
**`get(): Promise<DatabaseRecord[]>`**
Execute and return all matching rows.
**`first(): Promise<DatabaseRecord | null>`**
Execute and return the first matching row.
**`find(value, column?): Promise<DatabaseRecord | null>`**
Find a row by column value (default: id).
**`insert(data): Promise<DatabaseRecord[]>`**
Insert one or more rows.
**`update(data): Promise<any>`**
Update rows matching the conditions.
**`delete(): Promise<any>`**
Delete rows matching the conditions.
## Multi-Database Support
Dbcube Query Builder works with multiple database engines:
```typescript
// MySQL
const mysqlDb = new Database("mysql_database");
// PostgreSQL
const postgresDb = new Database("postgres_database");
// SQLite
const sqliteDb = new Database("sqlite_database");
// MongoDB
const mongoDb = new Database("mongo_database");
// Same API works across all databases
const users = await mysqlDb.table("users").get();
const posts = await postgresDb.table("posts").get();
const logs = await sqliteDb.table("logs").get();
const analytics = await mongoDb.table("analytics").get();
```
## Advanced Features
### Complex Query Example
```typescript
// Complex business query
const monthlyReport = await db.table("orders")
.join("users", "orders.user_id", "=", "users.id")
.join("order_items", "orders.id", "=", "order_items.order_id")
.join("products", "order_items.product_id", "=", "products.id")
.select([
"users.country",
"products.category"
])
.sum("order_items.quantity * order_items.price")
.where("orders.status", "=", "completed")
.whereBetween("orders.created_at", ["2024-01-01", "2024-01-31"])
.groupBy("users.country")
.groupBy("products.category")
.orderBy("sum", "DESC")
.limit(10)
.get();
```
### Method Chaining
All query builder methods return the Table instance, enabling fluent method chaining:
```typescript
const result = await db
.table("users")
.select(["name", "email", "country"])
.where("active", "=", true)
.whereNotNull("email_verified_at")
.whereGroup((query) => {
query.where("subscription", "=", "premium")
.orWhere("total_orders", ">", 10);
})
.join("user_profiles", "users.id", "=", "user_profiles.user_id")
.orderBy("users.created_at", "DESC")
.limit(50)
.page(1)
.get();
```
## License
This project is licensed under the MIT License - see the LICENSE file for details.
---
## Documentación en español
## Tabla de contenidos 🚀
- [Introducción](#introducción)
- [Características](#características-1)
- [Instalación](#instalación-1)
- [Configuración](#configuración-1)
- [Uso básico](#uso-básico)
- [Conexión a la base de datos](#conexión-a-la-base-de-datos)
- [Operaciones de tabla](#operaciones-de-tabla)
- [Operaciones CRUD](#operaciones-crud-1)
- [Insertar datos](#insertar-datos)
- [Seleccionar datos](#seleccionar-datos)
- [Actualizar datos](#actualizar-datos)
- [Eliminar datos](#eliminar-datos)
- [Consultas avanzadas](#consultas-avanzadas-1)
- [Consulta con WHERE](#consulta-con-where)
- [Consulta con OR WHERE](#consulta-con-or-where)
- [Consulta con grupos de condiciones WHERE](#consulta-con-grupos-de-condiciones-where)
- [Consulta con BETWEEN](#consulta-con-between)
- [Consulta con IN](#consulta-con-in)
- [Consulta con IS NULL / IS NOT NULL](#consulta-con-is-null--is-not-null)
- [Consulta con JOIN](#consulta-con-join)
- [Consulta con LEFT JOIN](#consulta-con-left-join)
- [Consulta con RIGHT JOIN](#consulta-con-right-join)
- [Consulta con ORDER BY](#consulta-con-order-by)
- [Consulta con LIMIT y OFFSET](#consulta-con-limit-y-offset-paginación)
- [Consulta con GROUP BY](#consulta-con-group-by)
- [Consulta con DISTINCT](#consulta-con-distinct)
- [Funciones de agregación](#funciones-de-agregación-1)
- [count](#count-1)
- [sum](#sum-1)
- [avg](#avg-1)
- [max](#max-1)
- [min](#min-1)
- [Buscar registros](#buscar-registros)
- [find](#find-1)
- [first](#first-1)
- [Campos calculados y triggers](#campos-calculados-y-triggers)
- [Ejecutar consultas SQL crudas](#ejecutar-consultas-sql-crudas)
- [Manejo de errores](#manejo-de-errores)
- [API completa](#api-completa-1)
- [Clase Database](#clase-database)
- [Clase Table](#clase-table)
- [Soporte multi-base de datos](#soporte-multi-base-de-datos)
- [Características avanzadas](#características-avanzadas)
- [Licencia](#licencia)
## Introducción
`/query-builder` es una biblioteca de Node.js ligera, flexible y fluida diseñada para construir consultas a través de múltiples motores de base de datos, incluyendo MySQL, PostgreSQL, SQLite y MongoDB, usando JavaScript/TypeScript.
Su diseño agnóstico te permite generar operaciones de manipulación de datos (DML) y definición de datos (DDL) con una sintaxis limpia y encadenable, sin sacrificar potencia o expresividad. Está diseñada para trabajar perfectamente en entornos SQL y NoSQL, proporcionando una capa de abstracción consistente a través de diferentes tecnologías de almacenamiento mientras aprovecha las capacidades nativas de cada motor.
**Diferenciador clave**: A diferencia de otros constructores de consultas que se enfocan en un solo tipo de base de datos, Dbcube Query Builder proporciona una API unificada que funciona a través de bases de datos SQL y NoSQL, haciéndola perfecta para arquitecturas políglotas modernas.
## Características
- **Soporte multi-base de datos**: MySQL, PostgreSQL, SQLite y MongoDB
- **API fluida** para construir consultas SQL con métodos encadenables
- **Construcción de consultas type-safe** con soporte TypeScript
- **Operaciones CRUD completas**: SELECT, INSERT, UPDATE, DELETE
- **Condiciones WHERE avanzadas** (AND, OR, grupos, BETWEEN, IN, verificaciones NULL)
- **Soporte JOIN**: INNER, LEFT, RIGHT joins
- **Agregaciones**: COUNT, SUM, AVG, MAX, MIN
- **Modificadores de consulta**: ORDER BY, GROUP BY, DISTINCT, LIMIT/OFFSET
- **Campos calculados**: Cálculos dinámicos de campos
- **Triggers**: Hooks antes/después de operaciones
- **API asíncrona basada en promesas**
- **Connection pooling** a través de /core
- **Manejo de errores** con mensajes descriptivos
- **Compatibilidad multiplataforma** (Windows, macOS, Linux)
## Instalación
```bash
npm install /query-builder
```
## Configuración
Dbcube Query Builder funciona con el ecosistema Dbcube. Asegúrate de tener la configuración adecuada de base de datos a través de /core.
```typescript
// No se necesita configuración explícita - funciona a través de Dbcube core
import { Database } from "@dbcube/query-builder";
```
## Uso básico
### Conexión a la base de datos
La conexión se gestiona automáticamente a través del sistema core de Dbcube.
```typescript
import { Database } from "@dbcube/query-builder";
// Crear una instancia de base de datos
const db = new Database("mi_base_de_datos");
// Obtener una referencia de tabla
const tablaUsuarios = db.table("usuarios");
```
## Operaciones de tabla
Dbcube Query Builder se enfoca en operaciones de datos. La creación de tablas y gestión de esquemas se maneja por otros componentes de Dbcube.
```typescript
// Acceder a tabla para operaciones
const tablaUsuarios = db.table("usuarios");
```
## Operaciones CRUD
### Insertar datos
Utiliza el método `insert` para añadir nuevos registros a una tabla.
```typescript
// Insertar un solo registro
const nuevoUsuario = await tablaUsuarios.insert([
{ nombre: "Alicia", email: "alicia@ejemplo.com", edad: 28 }
]);
// Insertar múltiples registros
const nuevosUsuarios = await tablaUsuarios.insert([
{ nombre: "Alicia", email: "alicia@ejemplo.com", edad: 28 },
{ nombre: "Roberto", email: "roberto@ejemplo.com", edad: 32 },
{ nombre: "Carlos", email: "carlos@ejemplo.com", edad: 35 }
]);
console.log(nuevosUsuarios);
// Devuelve los registros insertados con IDs generados
```
### Seleccionar datos
Utiliza el método `select` para especificar columnas y `get()` para recuperar datos.
```typescript
// Seleccionar todas las columnas
const todosUsuarios = await tablaUsuarios.get();
// Seleccionar columnas específicas
const usuarios = await tablaUsuarios
.select(["id", "nombre", "email"])
.get();
// Seleccionar con condiciones
const usuariosActivos = await tablaUsuarios
.select(["nombre", "email"])
.where("estado", "=", "activo")
.orderBy("fecha_creacion", "DESC")
.limit(10)
.get();
console.log(usuarios);
// [{ id: 1, nombre: 'Alicia', email: 'alicia@ejemplo.com' }, ...]
```
### Actualizar datos
Utiliza el método `update` para modificar registros existentes. **Requiere condiciones WHERE**.
```typescript
// Actualizar un campo
await tablaUsuarios
.where("id", "=", 1)
.update({ edad: 29 });
// Actualizar múltiples campos
await tablaUsuarios
.where("email", "=", "alicia@ejemplo.com")
.update({
nombre: "Alicia García",
edad: 29,
actualizado_en: new Date()
});
// Actualizar con condiciones complejas
await tablaUsuarios
.where("edad", ">", 30)
.where("estado", "=", "inactivo")
.update({ estado: "archivado" });
```
### Eliminar datos
Utiliza el método `delete` para eliminar registros. **Requiere condiciones WHERE**.
```typescript
// Eliminar registro específico
await tablaUsuarios
.where("id", "=", 2)
.delete();
// Eliminar con condiciones
await tablaUsuarios
.where("estado", "=", "eliminado")
.where("fecha_creacion", "<", "2023-01-01")
.delete();
```
## Consultas avanzadas
### Consulta con WHERE
Filtra registros utilizando el método `where` con varios operadores.
```typescript
// Comparaciones básicas
const usuariosAdultos = await tablaUsuarios.where("edad", ">", 18).get();
const coincidenciaExacta = await tablaUsuarios.where("nombre", "=", "Alicia").get();
const noIgual = await tablaUsuarios.where("estado", "!=", "eliminado").get();
// Operaciones con strings
const usuariosGmail = await tablaUsuarios.where("email", "LIKE", "%@gmail.com").get();
console.log(usuariosAdultos);
// [{ id: 1, nombre: 'Alicia', edad: 28 }, ...]
```
### Consulta con OR WHERE
Utiliza `orWhere` para añadir condiciones OR a tu consulta.
```typescript
const usuarios = await tablaUsuarios
.where("edad", ">", 25)
.orWhere("nombre", "=", "Alicia")
.get();
// Condiciones OR complejas
const usuariosPremium = await tablaUsuarios
.where("suscripcion", "=", "premium")
.orWhere("compras_totales", ">", 1000)
.orWhere("miembro_desde", "<", "2020-01-01")
.get();
console.log(usuarios);
// [{ id: 1, nombre: 'Alicia', edad: 28 }, ...]
```
### Consulta con grupos de condiciones WHERE
Agrupa condiciones utilizando `whereGroup` para lógica compleja.
```typescript
// (edad > 25 OR nombre = 'Juana') AND estado = 'activo'
const usuarios = await tablaUsuarios
.whereGroup((query) => {
query.where("edad", ">", 25).orWhere("nombre", "=", "Juana");
})
.where("estado", "=", "activo")
.get();
// Grupos anidados
const consultaCompleja = await tablaUsuarios
.whereGroup((query) => {
query.where("pais", "=", "ES").orWhere("pais", "=", "MX");
})
.where("activo", "=", true)
.whereGroup((query) => {
query.where("edad", ">=", 21).orWhere("verificado", "=", true);
})
.get();
```
### Consulta con BETWEEN
Busca valores dentro de un rango utilizando `whereBetween`.
```typescript
// Edad entre 25 y 35
const usuarios = await tablaUsuarios.whereBetween("edad", [25, 35]).get();
// Rangos de fechas
const usuariosRecientes = await tablaUsuarios
.whereBetween("fecha_creacion", ["2024-01-01", "2024-12-31"])
.get();
console.log(usuarios);
// [{ id: 1, nombre: 'Alicia', edad: 28 }, { id: 2, nombre: 'Roberto', edad: 32 }]
```
### Consulta con IN
Busca valores que coincidan con un conjunto de valores utilizando `whereIn`.
```typescript
// IDs específicos
const usuarios = await tablaUsuarios.whereIn("id", [1, 3, 5]).get();
// Múltiples estados
const usuariosFiltrados = await tablaUsuarios
.whereIn("estado", ["activo", "pendiente", "verificado"])
.get();
// Valores string
const dominiosEmail = await tablaUsuarios
.whereIn("dominio_email", ["gmail.com", "yahoo.com", "hotmail.com"])
.get();
console.log(usuarios);
// [{ id: 1, nombre: 'Alicia', edad: 28 }, { id: 3, nombre: 'Carlos', edad: 35 }]
```
### Consulta con IS NULL / IS NOT NULL
Busca valores nulos o no nulos utilizando `whereNull` y `whereNotNull`.
```typescript
// Usuarios sin email
const usuariosSinEmail = await tablaUsuarios.whereNull("email").get();
// Usuarios con email
const usuariosConEmail = await tablaUsuarios.whereNotNull("email").get();
// Combinar con otras condiciones
const perfilesIncompletos = await tablaUsuarios
.whereNull("telefono")
.orWhere("avatar", "IS NULL")
.whereNotNull("email")
.get();
```
### Consulta con JOIN
Une tablas utilizando el método `join` para INNER JOIN.
```typescript
// INNER JOIN básico
const usuariosConPedidos = await tablaUsuarios
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.select(["usuarios.nombre", "pedidos.pedido_id", "pedidos.total"])
.get();
// Múltiples JOINs
const pedidosDetallados = await tablaUsuarios
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.join("items_pedido", "pedidos.id", "=", "items_pedido.pedido_id")
.join("productos", "items_pedido.producto_id", "=", "productos.id")
.select(["usuarios.nombre", "pedidos.pedido_id", "productos.nombre AS nombre_producto"])
.get();
console.log(usuariosConPedidos);
// [{ nombre: 'Alicia', pedido_id: 101, total: 150.00 }, ...]
```
### Consulta con LEFT JOIN
Realiza un left join utilizando el método `leftJoin`.
```typescript
// Incluir usuarios aunque no tengan pedidos
const usuariosConPedidos = await tablaUsuarios
.leftJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.select(["usuarios.nombre", "pedidos.pedido_id"])
.get();
// Left join con agregación
const contadorPedidosUsuarios = await tablaUsuarios
.leftJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.select(["usuarios.nombre"])
.count("pedidos.id")
.groupBy("usuarios.id")
.get();
console.log(usuariosConPedidos);
// [{ nombre: 'Alicia', pedido_id: 101 }, { nombre: 'Roberto', pedido_id: null }, ...]
```
### Consulta con RIGHT JOIN
Realiza un right join utilizando el método `rightJoin`.
```typescript
// Incluir pedidos aunque falten datos del usuario
const pedidosConUsuarios = await tablaUsuarios
.rightJoin("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.select(["usuarios.nombre", "pedidos.pedido_id"])
.get();
console.log(pedidosConUsuarios);
// [{ nombre: 'Alicia', pedido_id: 101 }, { nombre: null, pedido_id: 102 }, ...]
```
### Consulta con ORDER BY
Ordena resultados utilizando el método `orderBy`.
```typescript
// Ordenamiento de una columna
const usuariosOrdenados = await tablaUsuarios
.orderBy("nombre", "ASC")
.get();
// Ordenamiento de múltiples columnas
const ordenComplejo = await tablaUsuarios
.orderBy("pais", "ASC")
.orderBy("edad", "DESC")
.orderBy("nombre", "ASC")
.get();
// Ordenar con condiciones
const usuariosActivosRecientes = await tablaUsuarios
.where("estado", "=", "activo")
.orderBy("ultimo_acceso", "DESC")
.limit(20)
.get();
console.log(usuariosOrdenados);
// [{ id: 1, nombre: 'Alicia', ... }, { id: 2, nombre: 'Roberto', ... }]
```
### Consulta con LIMIT y OFFSET (paginación)
Limita el número de resultados e implementa paginación utilizando `limit` y `page`.
```typescript
// Límite simple
const primerosDiezUsuarios = await tablaUsuarios.limit(10).get();
// Paginación
const primeraPagina = await tablaUsuarios.limit(5).page(1).get();
const segundaPagina = await tablaUsuarios.limit(5).page(2).get();
// Paginación con ordenamiento
const usuariosPaginados = await tablaUsuarios
.orderBy("fecha_creacion", "DESC")
.limit(10)
.page(3) // Omitir los primeros 20 registros (páginas 1-2)
.get();
console.log(primeraPagina); // Registros 1-5
console.log(segundaPagina); // Registros 6-10
```
### Consulta con GROUP BY
Agrupa resultados utilizando el método `groupBy`.
```typescript
// Agrupamiento simple
const usuariosPorEdad = await tablaUsuarios
.select(["edad"])
.count("*")
.groupBy("edad")
.get();
// Múltiples columnas de agrupamiento
const usuariosPorPaisYEdad = await tablaUsuarios
.select(["pais", "edad"])
.count("*")
.groupBy("pais")
.groupBy("edad")
.get();
console.log(usuariosPorEdad);
// [{ edad: 28, count: 2 }, { edad: 32, count: 1 }]
```
### Consulta con DISTINCT
Recupera registros únicos utilizando el método `distinct`.
```typescript
// Valores únicos
const paisesUnicos = await tablaUsuarios
.distinct()
.select(["pais"])
.get();
// Distinct con condiciones
const paisesUsuariosActivos = await tablaUsuarios
.distinct()
.select(["pais"])
.where("estado", "=", "activo")
.get();
console.log(paisesUnicos);
// [{ pais: 'España' }, { pais: 'México' }, { pais: 'Argentina' }]
```
## Funciones de agregación
### count
Cuenta el número de registros.
```typescript
// Contar todos los registros
const totalUsuarios = await tablaUsuarios.count().first();
console.log(totalUsuarios); // { count: 150 }
// Contar columna específica
const usuariosConEmail = await tablaUsuarios.count("email").first();
// Contar con condiciones
const usuariosActivos = await tablaUsuarios
.where("estado", "=", "activo")
.count()
.first();
```
### sum
Calcula la suma de una columna.
```typescript
// Suma de edades
const edadTotal = await tablaUsuarios.sum("edad").first();
console.log(edadTotal); // { sum: 4250 }
// Suma con condiciones
const ingresosPremium = await tablaUsuarios
.join("pedidos", "usuarios.id", "=", "pedidos.usuario_id")
.where("usuarios.suscripcion", "=", "premium")
.sum("pedidos.total")
.first();
```
### avg
Calcula el promedio de una columna.
```typescript
// Edad promedio
const edadPromedio = await tablaUsuarios.avg("edad").first();
console.log(edadPromedio); // { avg: 28.33 }
// Promedio con agrupamiento
const edadPromedioPorPais = await tablaUsuarios
.select(["pais"])
.avg("edad")
.groupBy("pais")
.get();
```
### max
Encuentra el valor máximo en una columna.
```typescript
// Usuario más viejo
const edadMaxima = await tablaUsuarios.max("edad").first();
console.log(edadMaxima); // { max: 65 }
// Registro más reciente
const usuarioMasReciente = await tablaUsuarios.max("fecha_creacion").first();
```
### min
Encuentra el valor mínimo en una columna.
```typescript
// Usuario más joven
const edadMinima = await tablaUsuarios.min("edad").first();
console.log(edadMinima); // { min: 18 }
// Primer registro
const primerUsuario = await tablaUsuarios.min("fecha_creacion").first();
```
## Buscar registros
### find
Encuentra un registro por un valor específico de columna (por defecto 'id').
```typescript
// Buscar por ID (por defecto)
const usuario = await tablaUsuarios.find(1);
console.log(usuario);
// { id: 1, nombre: 'Alicia', email: 'alicia@ejemplo.com', edad: 28 }
// Buscar por columna específica
const usuarioPorEmail = await tablaUsuarios.find("alicia@ejemplo.com", "email");
// Find devuelve null si no se encuentra
const noExistente = await tablaUsuarios.find(999);
console.log(noExistente); // null
```
### first
Obtiene solo el primer registro que cumple con las condiciones.
```typescript
// Primer usuario que cumple la condición
const primerUsuario = await tablaUsuarios
.where("edad", ">", 25)
.orderBy("fecha_creacion", "ASC")
.first();
// Primer usuario en general
const cuentaMasAntigua = await tablaUsuarios
.orderBy("fecha_creacion", "ASC")
.first();
console.log(primerUsuario);
// { id: 1, nombre: 'Alicia', edad: 28, ... } o null si no hay coincidencia
```
## Campos calculados y triggers
Dbcube Query Builder soporta campos calculados y triggers para procesamiento avanzado de datos.
```typescript
// Habilitar campos calculados (procesados automáticamente)
await db.useComputes();
// Habilitar triggers
await db.useTriggers();
// Los triggers y campos calculados se configuran a través de otros componentes Dbcube
```
## Ejecutar consultas SQL crudas
Para consultas complejas que requieren SQL crudo, utiliza el motor subyacente.
```typescript
// Acceder al motor subyacente para consultas crudas
// (La implementación depende de tu configuración específica de Dbcube core)
// Nota: Las consultas SQL crudas evitan la capa de abstracción del query builder
// y son específicas de la base de datos
```
### Manejo de errores
La biblioteca proporciona manejo comprehensivo de errores con mensajes descriptivos.
```typescript
try {
// Esto arrojará un error - UPDATE requiere condiciones WHERE
await tablaUsuarios.update({ nombre: "Actualizado" });
} catch (error) {
console.error(error.message);
// "Debes especificar al menos una condición WHERE para realizar una actualización."
}
try {
// Esto arrojará un error - formato de datos inválido
await tablaUsuarios.insert("datos inválidos");
} catch (error) {
console.error(error.message);
// "El método insert requiere un array de objetos con pares clave-valor."
}
// Los errores de conexión y base de datos también se manejan apropiadamente
try {
const resultado = await tablaUsuarios.get();
} catch (error) {
// Errores de conexión a base de datos o ejecución de consultas
console.error("Error de base de datos:", error);
}
```
## API completa
### Clase Database
#### `new Database(name: string)`
Crea una nueva instancia de conexión a base de datos.
```typescript
const db = new Database("mi_base_de_datos");
```
#### `table(tableName: string): Table`
Devuelve una instancia Table para construir consultas en la tabla especificada.
```typescript
const tablaUsuarios = db.table("usuarios");
```
#### `useComputes(): Promise<void>`
Habilita el procesamiento de campos calculados para la base de datos.
```typescript
await db.useComputes();
```
#### `useTriggers(): Promise<void>`
Habilita el procesamiento de triggers para la base de datos.
```typescript
await db.useTriggers();
```
#### `connect(): Promise<void>`
Establece conexión a base de datos (manejado automáticamente).
#### `disconnect(): Promise<void>`
Cierra conexión a base de datos.
### Clase Table
Los métodos de la clase Table siguen la misma API que se documentó en la sección en inglés, con la funcionalidad idéntica.
## Soporte multi-base de datos
Dbcube Query Builder funciona con múltiples motores de base de datos:
```typescript
// MySQL
const mysqlDb = new Database("base_datos_mysql");
// PostgreSQL
const postgresDb = new Database("base_datos_postgres");
// SQLite
const sqliteDb = new Database("base_datos_sqlite");
// MongoDB
const mongoDb = new Database("base_datos_mongo");
// La misma API funciona a través de todas las bases de datos
const usuarios = await mysqlDb.table("usuarios").get();
const posts = await postgresDb.table("posts").get();
const logs = await sqliteDb.table("logs").get();
const analiticas = await mongoDb.table("analiticas").get();
```
## Características avanzadas
### Ejemplo de consulta compleja
```typescript
// Consulta de negocio compleja
const reporteMensual = await db.table("pedidos")
.join("usuarios", "pedidos.usuario_id", "=", "usuarios.id")
.join("items_pedido", "pedidos.id", "=", "items_pedido.pedido_id")
.join("productos", "items_pedido.producto_id", "=", "productos.id")
.select([
"usuarios.pais",
"productos.categoria"
])
.sum("items_pedido.cantidad * items_pedido.precio")
.where("pedidos.estado", "=", "completado")
.whereBetween("pedidos.fecha_creacion", ["2024-01-01", "2024-01-31"])
.groupBy("usuarios.pais")
.groupBy("productos.categoria")
.orderBy("sum", "DESC")
.limit(10)
.get();
```
### Encadenamiento de métodos
Todos los métodos del query builder devuelven la instancia Table, habilitando el encadenamiento fluido de métodos:
```typescript
const resultado = await db
.table("usuarios")
.select(["nombre", "email", "pais"])
.where("activo", "=", true)
.whereNotNull("email_verificado_en")
.whereGroup((query) => {
query.where("suscripcion", "=", "premium")
.orWhere("pedidos_totales", ">", 10);
})
.join("perfiles_usuario", "usuarios.id", "=", "perfiles_usuario.usuario_id")
.orderBy("usuarios.fecha_creacion", "DESC")
.limit(50)
.page(1)
.get();
```
## Licencia
Este proyecto está licenciado bajo la Licencia MIT - consulta el archivo LICENSE para más detalles.