La interfaz está diseñada para ser simple y de alto rendimiento, usando template literals etiquetados para consultas y ofreciendo características como agrupación de conexiones, transacciones y sentencias preparadas.
import { sql, SQL } from "bun";
// PostgreSQL (predeterminado)
const users = await sql`
SELECT * FROM users
WHERE active = ${true}
LIMIT ${10}
`;
// Con MySQL
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb");
const mysqlResults = await mysql`
SELECT * FROM users
WHERE active = ${true}
`;
// Con SQLite
const sqlite = new SQL("sqlite://myapp.db");
const sqliteResults = await sqlite`
SELECT * FROM users
WHERE active = ${1}
`;Características
- Template literals etiquetados para proteger contra inyección SQL
- Transacciones
- Parámetros nominales y posicionales
- Agrupación de conexiones
- Soporte para
BigInt - Soporte de autenticación SASL (SCRAM-SHA-256), MD5 y texto claro
- Tiempos de espera de conexión
- Devolver filas como objetos de datos, arrays de arrays o Buffer
- Soporte de protocolo binario lo hace más rápido
- Soporte TLS (y modo de autenticación)
- Configuración automática con variable de entorno
Soporte de Bases de Datos
Bun.SQL proporciona una API unificada para múltiples sistemas de bases de datos:
PostgreSQL
PostgreSQL se usa cuando:
- La cadena de conexión no coincide con los patrones de SQLite o MySQL (es el adaptador predeterminado)
- La cadena de conexión usa explícitamente los protocolos
postgres://opostgresql:// - No se proporciona ninguna cadena de conexión y las variables de entorno apuntan a PostgreSQL
import { sql } from "bun";
// Usa PostgreSQL si DATABASE_URL no está establecida o es una URL de PostgreSQL
await sql`SELECT ...`;
import { SQL } from "bun";
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
await pg`SELECT ...`;MySQL
El soporte de MySQL está integrado en Bun.SQL, proporcionando la misma interfaz de template literal etiquetado con compatibilidad completa para MySQL 5.7+ y MySQL 8.0+:
import { SQL } from "bun";
// Conexión MySQL
const mysql = new SQL("mysql://user:password@localhost:3306/database");
const mysql2 = new SQL("mysql2://user:password@localhost:3306/database"); // el protocolo mysql2 también funciona
// Usando objeto de opciones
const mysql3 = new SQL({
adapter: "mysql",
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
});
// Funciona con parámetros - usa automáticamente sentencias preparadas
const users = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Las transacciones funcionan igual que PostgreSQL
await mysql.begin(async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = ${userId}`;
});
// Inserciones masivas
const newUsers = [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
];
await mysql`INSERT INTO users ${mysql(newUsers)}`;Formatos de cadena de conexión de MySQL">
MySQL acepta varios formatos de URL para cadenas de conexión:
// Protocolo estándar mysql://
new SQL("mysql://user:pass@localhost:3306/database");
new SQL("mysql://user:pass@localhost/database"); // Puerto predeterminado 3306
// Protocolo mysql2:// (compatibilidad con el paquete npm mysql2)
new SQL("mysql2://user:pass@localhost:3306/database");
// Con parámetros de consulta
new SQL("mysql://user:pass@localhost/db?ssl=true");
// Conexión de socket Unix
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");Características específicas de MySQL">
Las bases de datos MySQL soportan:
- Sentencias preparadas: Creadas automáticamente para consultas parametrizadas con caché de sentencias
- Protocolo binario: Para mejor rendimiento con sentencias preparadas y manejo preciso de tipos
- Múltiples conjuntos de resultados: Soporte para procedimientos almacenados que devuelven múltiples conjuntos de resultados
- Plugins de autenticación: Soporte para mysql_native_password, caching_sha2_password (predeterminado de MySQL 8.0), y sha256_password
- Conexiones SSL/TLS: Modos SSL configurables similares a PostgreSQL
- Atributos de conexión: Información del cliente enviada al servidor para monitoreo
- Pipelining de consultas: Ejecutar múltiples sentencias preparadas sin esperar respuestas
SQLite
El soporte de SQLite está integrado en Bun.SQL, proporcionando la misma interfaz de template literal etiquetado:
import { SQL } from "bun";
// Base de datos en memoria
const memory = new SQL(":memory:");
const memory2 = new SQL("sqlite://:memory:");
// Base de datos basada en archivos
const sql1 = new SQL("sqlite://myapp.db");
// Usando objeto de opciones
const sql2 = new SQL({
adapter: "sqlite",
filename: "./data/app.db",
});
// Para nombres de archivos simples, especifica el adaptador explícitamente
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });Formatos de cadena de conexión de SQLite">
SQLite acepta varios formatos de URL para cadenas de conexión:
// Protocolo estándar sqlite://
new SQL("sqlite://path/to/database.db");
new SQL("sqlite:path/to/database.db"); // Sin barras
// Protocolo file:// (también reconocido como SQLite)
new SQL("file://path/to/database.db");
new SQL("file:path/to/database.db");
// Base de datos especial :memory:
new SQL(":memory:");
new SQL("sqlite://:memory:");
new SQL("file://:memory:");
// Rutas relativas y absolutas
new SQL("sqlite://./local.db"); // Relativo al directorio actual
new SQL("sqlite://../parent/db.db"); // Directorio padre
new SQL("sqlite:///absolute/path.db"); // Ruta absoluta
// Con parámetros de consulta
new SQL("sqlite://data.db?mode=ro"); // Modo solo lectura
new SQL("sqlite://data.db?mode=rw"); // Modo lectura-escritura (sin crear)
new SQL("sqlite://data.db?mode=rwc"); // Modo lectura-escritura-crear (predeterminado)NOTE
Los nombres de archivos simples sin un protocolo (como `"myapp.db"`) requieren especificar explícitamente `{ adapter: "sqlite" }` para evitar ambigüedad con PostgreSQL.Opciones específicas de SQLite">
Las bases de datos SQLite soportan opciones de configuración adicionales:
const sql = new SQL({
adapter: "sqlite",
filename: "app.db",
// Opciones específicas de SQLite
readonly: false, // Abrir en modo solo lectura
create: true, // Crear base de datos si no existe
readwrite: true, // Abrir para lectura y escritura
// Opciones adicionales de Bun:sqlite
strict: true, // Habilitar modo estricto
safeIntegers: false, // Usar números de JavaScript para enteros
});Los parámetros de consulta en la URL se analizan para establecer estas opciones:
?mode=ro→readonly: true?mode=rw→readonly: false, create: false?mode=rwc→readonly: false, create: true(predeterminado)
Insertar datos
Puedes pasar valores de JavaScript directamente al template literal SQL y el escape se manejará por ti.
import { sql } from "bun";
// Inserción básica con valores directos
const [user] = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING *
`;
// Usando el ayudante de objeto para una sintaxis más limpia
const userData = {
name: "Alice",
email: "alice@example.com",
};
const [newUser] = await sql`
INSERT INTO users ${sql(userData)}
RETURNING *
`;
// Se expande a: INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')Inserción Masiva
También puedes pasar arrays de objetos al template literal SQL y se expandirá a una sentencia INSERT INTO ... VALUES ....
const users = [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
{ name: "Charlie", email: "charlie@example.com" },
];
await sql`INSERT INTO users ${sql(users)}`;Seleccionar columnas para insertar
Puedes usar sql(object, ...string) para seleccionar qué columnas insertar. Cada una de las columnas debe estar definida en el objeto.
const user = {
name: "Alice",
email: "alice@example.com",
age: 25,
};
await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// Solo inserta las columnas name y email, ignorando otros camposResultados de Consultas
Por defecto, el cliente SQL de Bun devuelve resultados de consultas como arrays de objetos, donde cada objeto representa una fila con nombres de columnas como claves. Sin embargo, hay casos donde podrías querer los datos en un formato diferente. El cliente proporciona dos métodos adicionales para este propósito.
Formato sql``.values()
El método sql``.values() devuelve filas como arrays de valores en lugar de objetos. Cada fila se convierte en un array donde los valores están en el mismo orden que las columnas en tu consulta.
const rows = await sql`SELECT * FROM users`.values();
console.log(rows);Esto devuelve algo como:
[
["Alice", "alice@example.com"],
["Bob", "bob@example.com"],
];sql``.values() es especialmente útil si se devuelven nombres de columnas duplicados en los resultados de la consulta. Cuando se usan objetos (el predeterminado), el último nombre de columna se usa como clave en el objeto, lo que significa que los nombres de columnas duplicados se sobrescriben entre sí, pero cuando se usa sql``.values(), cada columna está presente en el array para que puedas acceder a los valores de columnas duplicadas por índice.
Formato sql``.raw()
El método .raw() devuelve filas como arrays de objetos Buffer. Esto puede ser útil para trabajar con datos binarios o por razones de rendimiento.
const rows = await sql`SELECT * FROM users`.raw();
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]Fragmentos SQL
Una necesidad común en aplicaciones de bases de datos es la capacidad de construir consultas dinámicamente basadas en condiciones de tiempo de ejecución. Bun proporciona formas seguras de hacer esto sin riesgo de inyección SQL.
Nombres de Tablas Dinámicos
Cuando necesitas referenciar tablas o esquemas dinámicamente, usa el ayudante sql() para asegurar el escape apropiado:
// Referenciar tablas de forma segura dinámicamente
await sql`SELECT * FROM ${sql("users")}`;
// Con calificación de esquema
await sql`SELECT * FROM ${sql("public.users")}`;Consultas Condicionales
Puedes usar el ayudante sql() para construir consultas con cláusulas condicionales. Esto te permite crear consultas flexibles que se adaptan a las necesidades de tu aplicación:
// Cláusulas WHERE opcionales
const filterAge = true;
const minAge = 21;
const ageFilter = sql`AND age > ${minAge}`;
await sql`
SELECT * FROM users
WHERE active = ${true}
${filterAge ? ageFilter : sql``}
`;Columnas dinámicas en actualizaciones
Puedes usar sql(object, ...string) para seleccionar qué columnas actualizar. Cada una de las columnas debe estar definida en el objeto. Si las columnas no están informadas, se usarán todas las claves para actualizar la fila.
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
// usa todas las claves del objeto para actualizar la fila
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;Valores dinámicos y where in
Las listas de valores también se pueden crear dinámicamente, haciendo que las consultas where in también sean simples. Opcionalmente puedes pasar un array de objetos e informar qué clave usar para crear la lista.
await sql`SELECT * FROM users WHERE id IN ${sql([1, 2, 3])}`;
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
await sql`SELECT * FROM users WHERE id IN ${sql(users, "id")}`;Ayudante sql.array
El ayudante sql.array crea literales de array de PostgreSQL desde arrays de JavaScript:
// Crear literales de array para PostgreSQL
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
// Genera: INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])
// Funciona con arrays numéricos también
await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
// Genera: SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])NOTE
`sql.array` es solo para PostgreSQL. Los arrays multidimensionales y elementos NULL pueden no estar soportados aún.sql``.simple()
El protocolo wire de PostgreSQL soporta dos tipos de consultas: "simple" y "extended". Las consultas simples pueden contener múltiples sentencias pero no soportan parámetros, mientras que las consultas extended (el predeterminado) soportan parámetros pero solo permiten una sentencia.
Para ejecutar múltiples sentencias en una sola consulta, usa sql``.simple():
// Múltiples sentencias en una consulta
await sql`
SELECT 1;
SELECT 2;
`.simple();Las consultas simples son a menudo útiles para migraciones de bases de datos y scripts de configuración.
Ten en cuenta que las consultas simples no pueden usar parámetros (${value}). Si necesitas parámetros, debes dividir tu consulta en sentencias separadas.
Consultas en archivos
Puedes usar el método sql.file para leer una consulta de un archivo y ejecutarla, si el archivo incluye $1, $2, etc puedes pasar parámetros a la consulta. Si no se usan parámetros puede ejecutar múltiples comandos por archivo.
const result = await sql.file("query.sql", [1, 2, 3]);Consultas Inseguras
Puedes usar la función sql.unsafe para ejecutar cadenas SQL sin procesar. Usa esto con precaución, ya que no escapará la entrada del usuario. Ejecutar más de un comando por consulta está permitido si no se usan parámetros.
// Múltiples comandos sin parámetros
const result = await sql.unsafe(`
SELECT ${userColumns} FROM users;
SELECT ${accountColumns} FROM accounts;
`);
// Usando parámetros (solo se permite un comando)
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);Ejecutar y Cancelar Consultas
El SQL de Bun es lazy, lo que significa que solo comenzará a ejecutarse cuando se espere o se ejecute con .execute(). Puedes cancelar una consulta que se está ejecutando actualmente llamando al método cancel() en el objeto de consulta.
const query = sql`SELECT * FROM users`.execute();
setTimeout(() => query.cancel(), 100);
await query;Variables de Entorno de Base de Datos
Los parámetros de conexión sql se pueden configurar usando variables de entorno. El cliente verifica estas variables en un orden específico de precedencia y detecta automáticamente el tipo de base de datos basándose en el formato de la cadena de conexión.
Detección Automática de Base de Datos
Cuando usas Bun.sql() sin argumentos o new SQL() con una cadena de conexión, el adaptador se detecta automáticamente basándose en el formato de URL:
Detección Automática de MySQL
MySQL se selecciona automáticamente cuando la cadena de conexión coincide con estos patrones:
mysql://...- URLs de protocolo MySQLmysql2://...- URLs de protocolo MySQL2 (alias de compatibilidad)
// Todos estos usan MySQL automáticamente (sin adaptador necesario)
const sql1 = new SQL("mysql://user:pass@localhost/mydb");
const sql2 = new SQL("mysql2://user:pass@localhost:3306/mydb");
// Funciona con la variable de entorno DATABASE_URL
DATABASE_URL="mysql://user:pass@localhost/mydb" bun run app.js
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" bun run app.jsDetección Automática de SQLite
SQLite se selecciona automáticamente cuando la cadena de conexión coincide con estos patrones:
:memory:- Base de datos en memoriasqlite://...- URLs de protocolo SQLitesqlite:...- Protocolo SQLite sin barrasfile://...- URLs de protocolo de archivofile:...- Protocolo de archivo sin barras
// Todos estos usan SQLite automáticamente (sin adaptador necesario)
const sql1 = new SQL(":memory:");
const sql2 = new SQL("sqlite://app.db");
const sql3 = new SQL("file://./database.db");
// Funciona con la variable de entorno DATABASE_URL
DATABASE_URL=":memory:" bun run app.js
DATABASE_URL="sqlite://myapp.db" bun run app.js
DATABASE_URL="file://./data/app.db" bun run app.jsDetección Automática de PostgreSQL
PostgreSQL es el predeterminado para cadenas de conexión que no coinciden con los patrones de MySQL o SQLite:
# PostgreSQL se detecta para estos patrones
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" bun run app.js
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" bun run app.js
# O cualquier URL que no coincida con los patrones de MySQL o SQLite
DATABASE_URL="localhost:5432/mydb" bun run app.jsVariables de Entorno de MySQL
Las conexiones MySQL se pueden configurar vía variables de entorno:
# URL de conexión principal (verificada primero)
MYSQL_URL="mysql://user:pass@localhost:3306/mydb"
# Alternativa: DATABASE_URL con protocolo MySQL
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb"Si no se proporciona ninguna URL de conexión, MySQL verifica estos parámetros individuales:
| Variable de Entorno | Valor Predeterminado | Descripción |
|---|---|---|
MYSQL_HOST | localhost | Host de la base de datos |
MYSQL_PORT | 3306 | Puerto de la base de datos |
MYSQL_USER | root | Usuario de la base de datos |
MYSQL_PASSWORD | (vacío) | Contraseña de la base de datos |
MYSQL_DATABASE | mysql | Nombre de la base de datos |
MYSQL_URL | (vacío) | URL de conexión principal MySQL |
TLS_MYSQL_DATABASE_URL | (vacío) | URL de conexión SSL/TLS |
Variables de Entorno de PostgreSQL
Las siguientes variables de entorno se pueden usar para definir la conexión de PostgreSQL:
| Variable de Entorno | Descripción |
|---|---|
POSTGRES_URL | URL de conexión principal para PostgreSQL |
DATABASE_URL | URL de conexión alternativa (auto-detectada) |
PGURL | URL de conexión alternativa |
PG_URL | URL de conexión alternativa |
TLS_POSTGRES_DATABASE_URL | URL de conexión SSL/TLS |
TLS_DATABASE_URL | URL de conexión SSL/TLS alternativa |
Si no se proporciona ninguna URL de conexión, el sistema verifica los siguientes parámetros individuales:
| Variable de Entorno | Variables de Respaldo | Valor Predeterminado | Descripción |
|---|---|---|---|
PGHOST | - | localhost | Host de la BD |
PGPORT | - | 5432 | Puerto de la BD |
PGUSERNAME | PGUSER, USER, USERNAME | postgres | Usuario de la BD |
PGPASSWORD | - | (vacío) | Contraseña de la BD |
PGDATABASE | - | nombre de usuario | Nombre de la BD |
Variables de Entorno de SQLite
Las conexiones SQLite se pueden configurar vía DATABASE_URL cuando contiene una URL compatible con SQLite:
# Todos estos se reconocen como SQLite
DATABASE_URL=":memory:"
DATABASE_URL="sqlite://./app.db"
DATABASE_URL="file:///absolute/path/to/db.sqlite"Nota: Las variables de entorno específicas de PostgreSQL (POSTGRES_URL, PGHOST, etc.) se ignoran cuando se usa SQLite.
Preconexión en Tiempo de Ejecución
Bun puede preconectarse a PostgreSQL al inicio para mejorar el rendimiento estableciendo conexiones de bases de datos antes de que se ejecute el código de tu aplicación. Esto es útil para reducir la latencia de conexión en la primera consulta de base de datos.
# Habilitar preconexión de PostgreSQL
bun --sql-preconnect index.js
# Funciona con la variable de entorno DATABASE_URL
DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.js
# Se puede combinar con otros flags de tiempo de ejecución
bun --sql-preconnect --hot index.jsEl flag --sql-preconnect establecerá automáticamente una conexión de PostgreSQL usando tus variables de entorno configuradas al inicio. Si la conexión falla, no colapsará tu aplicación: el error se manejará gracefulmente.
Opciones de Conexión
Puedes configurar tu conexión de base de datos manualmente pasando opciones al constructor SQL. Las opciones varían dependiendo del adaptador de base de datos:
Opciones de MySQL
import { SQL } from "bun";
const sql = new SQL({
// Requerido para MySQL cuando se usa objeto de opciones
adapter: "mysql",
// Detalles de conexión
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Conexión de socket Unix (alternativa a hostname/port)
// socket: "/var/run/mysqld/mysqld.sock",
// Configuración del pool de conexiones
max: 20, // Máximo de conexiones en el pool (predeterminado: 10)
idleTimeout: 30, // Cerrar conexiones inactivas después de 30s
maxLifetime: 0, // Tiempo de vida de conexión en segundos (0 = para siempre)
connectionTimeout: 30, // Tiempo de espera al establecer nuevas conexiones
// Opciones SSL/TLS
ssl: "prefer", // o "disable", "require", "verify-ca", "verify-full"
// tls: {
// rejectUnauthorized: true,
// ca: "path/to/ca.pem",
// key: "path/to/key.pem",
// cert: "path/to/cert.pem",
// },
// Callbacks
onconnect: client => {
console.log("Conectado a MySQL");
},
onclose: (client, err) => {
if (err) {
console.error("Error de conexión MySQL:", err);
} else {
console.log("Conexión MySQL cerrada");
}
},
});Opciones de PostgreSQL
import { SQL } from "bun";
const sql = new SQL({
// Detalles de conexión (el adaptador se auto-detecta como PostgreSQL)
url: "postgres://user:pass@localhost:5432/dbname",
// Parámetros de conexión alternativos
hostname: "localhost",
port: 5432,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Configuración del pool de conexiones
max: 20, // Máximo de conexiones en el pool
idleTimeout: 30, // Cerrar conexiones inactivas después de 30s
maxLifetime: 0, // Tiempo de vida de conexión en segundos (0 = para siempre)
connectionTimeout: 30, // Tiempo de espera al establecer nuevas conexiones
// Opciones SSL/TLS
tls: true,
// tls: {
// rejectUnauthorized: true,
// requestCert: true,
// ca: "path/to/ca.pem",
// key: "path/to/key.pem",
// cert: "path/to/cert.pem",
// checkServerIdentity(hostname, cert) {
// ...
// },
// },
// Callbacks
onconnect: client => {
console.log("Conectado a PostgreSQL");
},
onclose: client => {
console.log("Conexión PostgreSQL cerrada");
},
});Opciones de SQLite
import { SQL } from "bun";
const sql = new SQL({
// Requerido para SQLite
adapter: "sqlite",
filename: "./data/app.db", // o ":memory:" para base de datos en memoria
// Modos de acceso específicos de SQLite
readonly: false, // Abrir en modo solo lectura
create: true, // Crear base de datos si no existe
readwrite: true, // Permitir operaciones de lectura y escritura
// Manejo de datos de SQLite
strict: true, // Habilitar modo estricto para mejor seguridad de tipos
safeIntegers: false, // Usar BigInt para enteros que exceden el rango de números JS
// Callbacks
onconnect: client => {
console.log("Base de datos SQLite abierta");
},
onclose: client => {
console.log("Base de datos SQLite cerrada");
},
});Notas de Conexión SQLite">
- Agrupación de Conexiones: SQLite no usa agrupación de conexiones ya que es una base de datos basada en archivos. Cada instancia
SQLrepresenta una única conexión. - Transacciones: SQLite soporta transacciones anidadas a través de savepoints, similar a PostgreSQL.
- Acceso Concurrente: SQLite maneja el acceso concurrente a través de bloqueo de archivos. Usa modo WAL para mejor concurrencia.
- Bases de Datos en Memoria: Usar
:memory:crea una base de datos temporal que existe solo durante el tiempo de vida de la conexión.
Contraseñas Dinámicas
Cuando los clientes necesitan usar esquemas de autenticación alternativos como tokens de acceso o conexiones a bases de datos con contraseñas rotativas, proporciona una función sincrónica o asíncrona que resolverá el valor de la contraseña dinámica en el momento de la conexión.
import { SQL } from "bun";
const sql = new SQL(url, {
// Otra configuración de conexión
...
// Función de contraseña para el usuario de la base de datos
password: async () => await signer.getAuthToken(),
});Características Específicas de SQLite
Ejecución de Consultas
SQLite ejecuta consultas sincrónicamente, a diferencia de PostgreSQL que usa I/O asíncrono. Sin embargo, la API permanece consistente usando Promesas:
const sqlite = new SQL("sqlite://app.db");
// Funciona igual que PostgreSQL, pero se ejecuta sincrónicamente bajo el capó
const users = await sqlite`SELECT * FROM users`;
// Los parámetros funcionan idénticamente
const user = await sqlite`SELECT * FROM users WHERE id = ${userId}`;Pragmas de SQLite
Puedes usar sentencias PRAGMA para configurar el comportamiento de SQLite:
const sqlite = new SQL("sqlite://app.db");
// Habilitar claves foráneas
await sqlite`PRAGMA foreign_keys = ON`;
// Establecer modo de journal a WAL para mejor concurrencia
await sqlite`PRAGMA journal_mode = WAL`;
// Verificar integridad
const integrity = await sqlite`PRAGMA integrity_check`;Diferencias de Tipos de Datos
SQLite tiene un sistema de tipos más flexible que PostgreSQL:
// SQLite almacena datos en 5 clases de almacenamiento: NULL, INTEGER, REAL, TEXT, BLOB
const sqlite = new SQL("sqlite://app.db");
// SQLite es más flexible con los tipos
await sqlite`
CREATE TABLE flexible (
id INTEGER PRIMARY KEY,
data TEXT, -- Puede almacenar números como cadenas
value NUMERIC, -- Puede almacenar enteros, reales o texto
blob BLOB -- Datos binarios
)
`;
// Los valores de JavaScript se convierten automáticamente
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;Transacciones
Para iniciar una nueva transacción, usa sql.begin. Este método funciona tanto para PostgreSQL como para SQLite. Para PostgreSQL, reserva una conexión dedicada del pool. Para SQLite, inicia una transacción en la única conexión.
El comando BEGIN se envía automáticamente, incluyendo cualquier configuración opcional que especifiques. Si ocurre un error durante la transacción, se activa un ROLLBACK para asegurar que el proceso continúe sin problemas.
Transacciones Básicas
await sql.begin(async tx => {
// Todas las consultas en esta función se ejecutan en una transacción
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`;
// La transacción se confirma automáticamente si no se lanzan errores
// Se revierte si ocurre algún error
});También es posible pipeline las solicitudes en una transacción si es necesario devolviendo un array con consultas desde la función callback así:
await sql.begin(async tx => {
return [
tx`INSERT INTO users (name) VALUES (${"Alice"})`,
tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`,
];
});Savepoints
Los savepoints en SQL crean puntos de control intermedios dentro de una transacción, permitiendo reversiones parciales sin afectar toda la operación. Son útiles en transacciones complejas, permitiendo recuperación de errores y manteniendo resultados consistentes.
await sql.begin(async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx.savepoint(async sp => {
// Esta parte se puede revertir por separado
await sp`UPDATE users SET status = 'active'`;
if (someCondition) {
throw new Error("Revertir al savepoint");
}
});
// Continuar con la transacción incluso si el savepoint se revirtió
await tx`INSERT INTO audit_log (action) VALUES ('user_created')`;
});Transacciones Distribuidas
Two-Phase Commit (2PC) es un protocolo de transacción distribuida donde la Fase 1 tiene al coordinador preparando nodos asegurando que los datos estén escritos y listos para confirmar, mientras que la Fase 2 finaliza con los nodos confirmando o revirtiendo basándose en la decisión del coordinador. Este proceso asegura durabilidad de datos y gestión apropiada de bloqueos.
En PostgreSQL y MySQL, las transacciones distribuidas persisten más allá de su sesión original, permitiendo que usuarios privilegiados o coordinadores las confirmen o reviertan más tarde. Esto soporta transacciones distribuidas robustas, procesos de recuperación y operaciones administrativas.
Cada sistema de base de datos implementa transacciones distribuidas de manera diferente:
PostgreSQL las soporta nativamente a través de transacciones preparadas, mientras que MySQL usa Transacciones XA.
Si ocurre alguna excepción durante la transacción distribuida y no se captura, el sistema revertirá automáticamente todos los cambios. Cuando todo procede normalmente, mantienes la flexibilidad de confirmar o revertir la transacción más tarde.
// Iniciar una transacción distribuida
await sql.beginDistributed("tx1", async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
});
// Más tarde, confirmar o revertir
await sql.commitDistributed("tx1");
// o
await sql.rollbackDistributed("tx1");Autenticación
Bun soporta autenticación SCRAM-SHA-256 (SASL), MD5 y texto claro. SASL se recomienda para mejor seguridad. Consulta Autenticación SASL de Postgres para más información.
Visión General de Modos SSL
PostgreSQL soporta diferentes modos SSL/TLS para controlar cómo se establecen las conexiones seguras. Estos modos determinan el comportamiento al conectar y el nivel de verificación de certificado realizado.
const sql = new SQL({
hostname: "localhost",
username: "user",
password: "password",
ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});| Modo SSL | Descripción |
|---|---|
disable | No se usa SSL/TLS. Las conexiones fallan si el servidor requiere SSL. |
prefer | Intenta SSL primero, vuelve a no-SSL si SSL falla. Modo predeterminado si no se especifica ninguno. |
require | Requiere SSL sin verificación de certificado. Falla si no se puede establecer SSL. |
verify-ca | Verifica que el certificado del servidor esté firmado por una CA de confianza. Falla si la verificación falla. |
verify-full | Modo más seguro. Verifica que el certificado y nombre de host coincidan. Protege contra certificados no confiables y ataques MITM. |
Usando Con Cadenas de Conexión
El modo SSL también se puede especificar en cadenas de conexión:
// Usando modo prefer
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");
// Usando modo verify-full
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");Agrupación de Conexiones
El cliente SQL de Bun gestiona automáticamente un pool de conexiones, que es un pool de conexiones de base de datos que se reutilizan para múltiples consultas. Esto ayuda a reducir la sobrecarga de establecer y cerrar conexiones para cada consulta, y también ayuda a gestionar el número de conexiones concurrentes a la base de datos.
const sql = new SQL({
// Configuración del pool
max: 20, // Máximo 20 conexiones concurrentes
idleTimeout: 30, // Cerrar conexiones inactivas después de 30s
maxLifetime: 3600, // Tiempo de vida máximo de conexión 1 hora
connectionTimeout: 10, // Tiempo de espera de conexión 10s
});No se hará ninguna conexión hasta que se haga una consulta.
const sql = Bun.SQL(); // no se crean conexiones
await sql`...`; // el pool se inicia hasta que se alcanza el máximo (si es posible), se usa la primera conexión disponible
await sql`...`; // se reutiliza la conexión anterior
// dos conexiones se usan ahora al mismo tiempo
await Promise.all([
sql`INSERT INTO users ${sql({ name: "Alice" })}`,
sql`UPDATE users SET name = ${user.name} WHERE id = ${user.id}`,
]);
await sql.close(); // esperar a que todas las consultas terminen y cerrar todas las conexiones del pool
await sql.close({ timeout: 5 }); // esperar 5 segundos y cerrar todas las conexiones del pool
await sql.close({ timeout: 0 }); // cerrar todas las conexiones del pool inmediatamenteConexiones Reservadas
Bun te permite reservar una conexión del pool, y devuelve un cliente que envuelve la única conexión. Esto se puede usar para ejecutar consultas en una conexión aislada.
// Obtener conexión exclusiva del pool
const reserved = await sql.reserve();
try {
await reserved`INSERT INTO users (name) VALUES (${"Alice"})`;
} finally {
// Importante: Liberar conexión de vuelta al pool
reserved.release();
}
// O usando Symbol.dispose
{
using reserved = await sql.reserve();
await reserved`SELECT 1`;
} // Automáticamente liberadaSentencias Preparadas
Por defecto, el cliente SQL de Bun crea automáticamente sentencias preparadas nombradas para consultas donde se puede inferir que la consulta es estática. Esto proporciona mejor rendimiento. Sin embargo, puedes cambiar este comportamiento estableciendo prepare: false en las opciones de conexión:
const sql = new SQL({
// ... otras opciones ...
prepare: false, // Deshabilitar persistencia de sentencias preparadas nombradas en el servidor
});Cuando prepare: false está establecido:
Las consultas aún se ejecutan usando el protocolo "extended", pero se ejecutan usando sentencias preparadas sin nombre, una sentencia preparada sin nombre dura solo hasta que se emite la siguiente sentencia Parse especificando la sentencia sin nombre como destino.
- El enlace de parámetros aún es seguro contra inyección SQL
- Cada consulta se analiza y planifica desde cero por el servidor
- Las consultas no se pipelinarán
Podrías querer usar prepare: false cuando:
- Usas PGBouncer en modo transacción (aunque desde PGBouncer 1.21.0, las sentencias preparadas nombradas a nivel de protocolo se soportan cuando se configuran apropiadamente)
- Depurando planes de ejecución de consultas
- Trabajando con SQL dinámico donde los planes de consulta necesitan regenerarse frecuentemente
- Más de un comando por consulta no se soportará (a menos que uses
sql``.simple())
Ten en cuenta que deshabilitar las sentencias preparadas puede impactar el rendimiento para consultas que se ejecutan frecuentemente con diferentes parámetros, ya que el servidor necesita analizar y planificar cada consulta desde cero.
Manejo de Errores
El cliente proporciona errores tipados para diferentes escenarios de fallo. Los errores son específicos de la base de datos y se extienden desde clases de error base:
Clases de Error
import { SQL } from "bun";
try {
await sql`SELECT * FROM users`;
} catch (error) {
if (error instanceof SQL.PostgresError) {
// Error específico de PostgreSQL
console.log(error.code); // Código de error de PostgreSQL
console.log(error.detail); // Mensaje de error detallado
console.log(error.hint); // Pista útil de PostgreSQL
} else if (error instanceof SQL.SQLiteError) {
// Error específico de SQLite
console.log(error.code); // Código de error de SQLite (ej. "SQLITE_CONSTRAINT")
console.log(error.errno); // Número de error de SQLite
console.log(error.byteOffset); // Desplazamiento de bytes en la sentencia SQL (si está disponible)
} else if (error instanceof SQL.SQLError) {
// Error SQL genérico (clase base)
console.log(error.message);
}
}Códigos de Error Específicos de PostgreSQL">
Errores de Conexión de PostgreSQL
| Errores de Conexión | Descripción |
|---|---|
ERR_POSTGRES_CONNECTION_CLOSED | La conexión fue terminada o nunca se estableció |
ERR_POSTGRES_CONNECTION_TIMEOUT | Falló al establecer la conexión dentro del tiempo de espera |
ERR_POSTGRES_IDLE_TIMEOUT | Conexión cerrada por inactividad |
ERR_POSTGRES_LIFETIME_TIMEOUT | La conexión excedió el tiempo de vida máximo |
ERR_POSTGRES_TLS_NOT_AVAILABLE | Conexión SSL/TLS no disponible |
ERR_POSTGRES_TLS_UPGRADE_FAILED | Falló al actualizar la conexión a SSL/TLS |
Errores de Autenticación
| Errores de Autenticación | Descripción |
|---|---|
ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2 | Falló la autenticación de contraseña |
ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD | El servidor solicitó método de auth desconocido |
ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD | El servidor solicitó método de auth no soportado |
ERR_POSTGRES_INVALID_SERVER_KEY | Clave de servidor inválida durante autenticación |
ERR_POSTGRES_INVALID_SERVER_SIGNATURE | Firma de servidor inválida |
ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64 | Codificación de firma SASL inválida |
ERR_POSTGRES_SASL_SIGNATURE_MISMATCH | Falló la verificación de firma SASL |
Errores de Consulta
| Errores de Consulta | Descripción |
|---|---|
ERR_POSTGRES_SYNTAX_ERROR | Sintaxis SQL inválida (extiende SyntaxError) |
ERR_POSTGRES_SERVER_ERROR | Error general del servidor PostgreSQL |
ERR_POSTGRES_INVALID_QUERY_BINDING | Enlace de parámetros inválido |
ERR_POSTGRES_QUERY_CANCELLED | La consulta fue cancelada |
ERR_POSTGRES_NOT_TAGGED_CALL | La consulta se llamó sin una llamada etiquetada |
Errores de Tipo de Datos
| Errores de Tipo de Datos | Descripción |
|---|---|
ERR_POSTGRES_INVALID_BINARY_DATA | Formato de datos binarios inválido |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE | Secuencia de bytes inválida |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING | Error de codificación |
ERR_POSTGRES_INVALID_CHARACTER | Carácter inválido en datos |
ERR_POSTGRES_OVERFLOW | Desbordamiento numérico |
ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT | Formato binario no soportado |
ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE | Tamaño de entero no soportado |
ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET | Arrays multidimensionales no soportados |
ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET | Valores NULL en arrays no soportados |
Errores de Protocolo
| Errores de Protocolo | Descripción |
|---|---|
ERR_POSTGRES_EXPECTED_REQUEST | Se esperaba solicitud de cliente |
ERR_POSTGRES_EXPECTED_STATEMENT | Se esperaba sentencia preparada |
ERR_POSTGRES_INVALID_BACKEND_KEY_DATA | Datos de clave de backend inválidos |
ERR_POSTGRES_INVALID_MESSAGE | Mensaje de protocolo inválido |
ERR_POSTGRES_INVALID_MESSAGE_LENGTH | Longitud de mensaje inválida |
ERR_POSTGRES_UNEXPECTED_MESSAGE | Tipo de mensaje inesperado |
Errores de Transacción
| Errores de Transacción | Descripción |
|---|---|
ERR_POSTGRES_UNSAFE_TRANSACTION | Se detectó operación de transacción insegura |
ERR_POSTGRES_INVALID_TRANSACTION_STATE | Estado de transacción inválido |
Errores Específicos de SQLite
Los errores de SQLite proporcionan códigos y números de error que corresponden a los códigos de error estándar de SQLite:
Códigos de Error Comunes de SQLite">
| Código de Error | errno | Descripción |
|---|---|---|
SQLITE_CONSTRAINT | 19 | Violación de restricción (UNIQUE, CHECK, NOT NULL, etc.) |
SQLITE_BUSY | 5 | La base de datos está bloqueada |
SQLITE_LOCKED | 6 | La tabla en la base de datos está bloqueada |
SQLITE_READONLY | 8 | Intento de escribir en una base de datos solo lectura |
SQLITE_IOERR | 10 | Error de I/O de disco |
SQLITE_CORRUPT | 11 | La imagen de disco de la base de datos está malformada |
SQLITE_FULL | 13 | La base de datos o disco está lleno |
SQLITE_CANTOPEN | 14 | No se puede abrir el archivo de base de datos |
SQLITE_PROTOCOL | 15 | Error de protocolo de bloqueo de base de datos |
SQLITE_SCHEMA | 17 | El esquema de la base de datos ha cambiado |
SQLITE_TOOBIG | 18 | La cadena o BLOB excede el límite de tamaño |
SQLITE_MISMATCH | 20 | Incompatibilidad de tipo de datos |
SQLITE_MISUSE | 21 | La librería se usó incorrectamente |
SQLITE_AUTH | 23 | Autorización denegada |
Ejemplo de manejo de errores:
const sqlite = new SQL("sqlite://app.db");
try {
await sqlite`INSERT INTO users (id, name) VALUES (1, 'Alice')`;
await sqlite`INSERT INTO users (id, name) VALUES (1, 'Bob')`; // ID duplicado
} catch (error) {
if (error instanceof SQL.SQLiteError) {
if (error.code === "SQLITE_CONSTRAINT") {
console.log("Violación de restricción:", error.message);
// Manejar violación de restricción única
}
}
}Números y BigInt
El cliente SQL de Bun incluye manejo especial para números grandes que exceden el rango de un entero de 53 bits. Así es como funciona:
import { sql } from "bun";
const [{ x, y }] = await sql`SELECT 9223372036854777 as x, 12345 as y`;
console.log(typeof x, x); // "string" "9223372036854777"
console.log(typeof y, y); // "number" 12345BigInt en lugar de Cadenas
Si necesitas números grandes como BigInt en lugar de cadenas, puedes habilitarlo estableciendo la opción bigint en true al inicializar el cliente SQL:
const sql = new SQL({
bigint: true,
});
const [{ x }] = await sql`SELECT 9223372036854777 as x`;
console.log(typeof x, x); // "bigint" 9223372036854777nHoja de Ruta
Todavía hay algunas cosas que no hemos terminado aún.
- Precarga de conexiones vía flag
--db-preconnectde la CLI de Bun - Transformaciones de nombres de columnas (ej.
snake_caseacamelCase). Esto está mayormente bloqueado en una implementación consciente de unicode para cambiar mayúsculas/minúsculas en C++ usandoWTF::Stringde WebKit. - Transformaciones de tipos de columnas
Características Específicas de Bases de Datos
Métodos de Autenticación
MySQL soporta múltiples plugins de autenticación que se negocian automáticamente:
mysql_native_password- Autenticación tradicional de MySQL, ampliamente compatiblecaching_sha2_password- Predeterminado en MySQL 8.0+, más seguro con intercambio de claves RSAsha256_password- Autenticación basada en SHA-256
El cliente maneja automáticamente el cambio de plugin de autenticación cuando lo solicita el servidor, incluyendo intercambio seguro de contraseñas sobre conexiones no-SSL.
Sentencias Preparadas y Rendimiento
MySQL usa sentencias preparadas del lado del servidor para todas las consultas parametrizadas:
// Esto crea automáticamente una sentencia preparada en el servidor
const user = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Las sentencias preparadas se almacenan en caché y reutilizan para consultas idénticas
for (const id of userIds) {
// La misma sentencia preparada se reutiliza
await mysql`SELECT * FROM users WHERE id = ${id}`;
}
// Pipelining de consultas - múltiples sentencias se envían sin esperar
const [users, orders, products] = await Promise.all([
mysql`SELECT * FROM users WHERE active = ${true}`,
mysql`SELECT * FROM orders WHERE status = ${"pending"}`,
mysql`SELECT * FROM products WHERE in_stock = ${true}`,
]);Múltiples Conjuntos de Resultados
MySQL puede devolver múltiples conjuntos de resultados desde consultas de múltiples sentencias:
const mysql = new SQL("mysql://user:pass@localhost/mydb");
// Consultas de múltiples sentencias con el método simple()
const multiResults = await mysql`
SELECT * FROM users WHERE id = 1;
SELECT * FROM orders WHERE user_id = 1;
`.simple();Conjuntos de Caracteres y Colaciones
Bun.SQL usa automáticamente el conjunto de caracteres utf8mb4 para conexiones MySQL, asegurando soporte Unicode completo incluyendo emojis. Este es el conjunto de caracteres recomendado para aplicaciones MySQL modernas.
Atributos de Conexión
Bun envía automáticamente información del cliente a MySQL para mejor monitoreo:
// Estos atributos se envían automáticamente:
// _client_name: "Bun"
// _client_version: <versión de bun>
// Puedes ver estos en performance_schema.session_connect_attrs de MySQLManejo de Tipos
Los tipos de MySQL se convierten automáticamente a tipos de JavaScript:
| Tipo de MySQL | Tipo de JavaScript | Notas |
|---|---|---|
| INT, TINYINT, MEDIUMINT | number | Dentro del rango seguro de enteros |
| BIGINT | string, number o BigInt | Si el valor cabe en tamaño i32/u32 será number de lo contrario string o BigInt basado en la opción bigint |
| DECIMAL, NUMERIC | string | Para preservar precisión |
| FLOAT, DOUBLE | number | |
| DATE | Date | Objeto Date de JavaScript |
| DATETIME, TIMESTAMP | Date | Con manejo de zona horaria |
| TIME | number | Total de microsegundos |
| YEAR | number | |
| CHAR, VARCHAR, VARSTRING, STRING | string | |
| TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXT | string | |
| TINY BLOB, MEDIUM BLOB, BLOG, LONG BLOB | string | Los tipos BLOB son alias para tipos TEXT |
| JSON | object/array | Automáticamente analizado |
| BIT(1) | boolean | BIT(1) en MySQL |
| GEOMETRY | string | Datos de geometría |
Diferencias con PostgreSQL
Aunque la API está unificada, hay algunas diferencias de comportamiento:
- Marcadores de posición de parámetros: MySQL usa
?internamente pero Bun convierte automáticamente al estilo$1, $2 - Cláusula RETURNING: MySQL no soporta RETURNING; usa
result.lastInsertRowido un SELECT separado - Tipos de array: MySQL no tiene tipos de array nativos como PostgreSQL
Características Específicas de MySQL
No hemos implementado el soporte para LOAD DATA INFILE aún
Características Específicas de PostgreSQL
No hemos implementado esto aún:
- Soporte para
COPY - Soporte para
LISTEN - Soporte para
NOTIFY
Tampoco hemos implementado algunas de las características más poco comunes como:
- Autenticación GSSAPI
- Soporte para
SCRAM-SHA-256-PLUS - Tipos Point y PostGIS
- Todos los tipos de arrays de enteros multidimensionales (solo se soportan un par de los tipos)
Patrones Comunes y Mejores Prácticas
Trabajar con Conjuntos de Resultados de MySQL
// Obtener ID de inserción después de INSERT
const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
console.log(result.lastInsertRowid); // LAST_INSERT_ID() de MySQL
// Manejar filas afectadas
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
console.log(updated.affectedRows); // Número de filas actualizadas
// Usar funciones específicas de MySQL
const now = await mysql`SELECT NOW() as current_time`;
const uuid = await mysql`SELECT UUID() as id`;Manejo de Errores de MySQL
try {
await mysql`INSERT INTO users (email) VALUES (${"duplicate@email.com"})`;
} catch (error) {
if (error.code === "ER_DUP_ENTRY") {
console.log("Se detectó entrada duplicada");
} else if (error.code === "ER_ACCESS_DENIED_ERROR") {
console.log("Acceso denegado");
} else if (error.code === "ER_BAD_DB_ERROR") {
console.log("La base de datos no existe");
}
// Los códigos de error de MySQL son compatibles con los paquetes mysql/mysql2
}Consejos de Rendimiento para MySQL
- Usa agrupación de conexiones: Establece un tamaño de pool
maxapropiado basado en tu carga de trabajo - Habilita sentencias preparadas: Están habilitadas por defecto y mejoran el rendimiento
- Usa transacciones para operaciones masivas: Agrupa consultas relacionadas en transacciones
- Indexa apropiadamente: MySQL depende mucho de índices para rendimiento de consultas
- Usa conjunto de caracteres
utf8mb4: Está establecido por defecto y maneja todos los caracteres Unicode
Preguntas Frecuentes
¿Por qué esto es `Bun.sql` y no `Bun.postgres`?
El plan era agregar más controladores de bases de datos en el futuro. Ahora con el soporte de MySQL agregado, esta API unificada soporta PostgreSQL, MySQL y SQLite.
¿Cómo sé qué adaptador de base de datos se está usando?
El adaptador se detecta automáticamente desde la cadena de conexión:
- Las URLs que comienzan con `mysql://` o `mysql2://` usan MySQL
- Las URLs que coinciden con patrones de SQLite (`:memory:`, `sqlite://`, `file://`) usan SQLite
- Todo lo demás predetermina a PostgreSQL
¿Se soportan los procedimientos almacenados de MySQL?">
Sí, los procedimientos almacenados se soportan completamente incluyendo parámetros OUT y múltiples conjuntos de resultados:
```ts
// Llamar procedimiento almacenado
const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;
// Obtener parámetro OUT
const outParam = await mysql`SELECT @total_orders as total`;
```
¿Puedo usar sintaxis SQL específica de MySQL?">
Sí, puedes usar cualquier sintaxis específica de MySQL:
```ts
// La sintaxis específica de MySQL funciona bien
await mysql`SET @user_id = ${userId}`;
await mysql`SHOW TABLES`;
await mysql`DESCRIBE users`;
await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
```
¿Por qué no solo usar una librería existente?
Paquetes npm como postgres.js, pg, y node-postgres se pueden usar en Bun también. Son grandes opciones.
Dos razones por qué:
- Creemos que es más simple para los desarrolladores tener un controlador de base de datos integrado en Bun. El tiempo que pasas comprando librerías es tiempo que podrías estar construyendo tu aplicación.
- Aprovechamos algunos internos del motor JavaScriptCore para hacerlo más rápido crear objetos que serían difíciles de implementar en una librería
Créditos
Un gran agradecimiento a @porsager por postgres.js por la inspiración para la interfaz de la API.