Skip to content

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.

ts
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:// o postgresql://
  • No se proporciona ninguna cadena de conexión y las variables de entorno apuntan a PostgreSQL
db.ts
ts
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+:

db.ts
ts
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:

ts
// 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:

ts
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:

ts
// 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:

ts
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=roreadonly: true
  • ?mode=rwreadonly: false, create: false
  • ?mode=rwcreadonly: false, create: true (predeterminado)

Insertar datos

Puedes pasar valores de JavaScript directamente al template literal SQL y el escape se manejará por ti.

ts
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 ....

ts
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.

ts
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 campos

Resultados 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.

ts
const rows = await sql`SELECT * FROM users`.values();
console.log(rows);

Esto devuelve algo como:

ts
[
  ["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.

ts
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:

ts
// 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:

ts
// 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.

ts
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.

ts
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:

ts
// 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():

ts
// 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.

ts
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.

ts
// 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.

ts
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 MySQL
  • mysql2://... - URLs de protocolo MySQL2 (alias de compatibilidad)
ts
// 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.js

Detecció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 memoria
  • sqlite://... - URLs de protocolo SQLite
  • sqlite:... - Protocolo SQLite sin barras
  • file://... - URLs de protocolo de archivo
  • file:... - Protocolo de archivo sin barras
ts
// 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.js

Detección Automática de PostgreSQL

PostgreSQL es el predeterminado para cadenas de conexión que no coinciden con los patrones de MySQL o SQLite:

bash
# 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.js

Variables de Entorno de MySQL

Las conexiones MySQL se pueden configurar vía variables de entorno:

bash
# 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 EntornoValor PredeterminadoDescripción
MYSQL_HOSTlocalhostHost de la base de datos
MYSQL_PORT3306Puerto de la base de datos
MYSQL_USERrootUsuario de la base de datos
MYSQL_PASSWORD(vacío)Contraseña de la base de datos
MYSQL_DATABASEmysqlNombre 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 EntornoDescripción
POSTGRES_URLURL de conexión principal para PostgreSQL
DATABASE_URLURL de conexión alternativa (auto-detectada)
PGURLURL de conexión alternativa
PG_URLURL de conexión alternativa
TLS_POSTGRES_DATABASE_URLURL de conexión SSL/TLS
TLS_DATABASE_URLURL 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 EntornoVariables de RespaldoValor PredeterminadoDescripción
PGHOST-localhostHost de la BD
PGPORT-5432Puerto de la BD
PGUSERNAMEPGUSER, USER, USERNAMEpostgresUsuario de la BD
PGPASSWORD-(vacío)Contraseña de la BD
PGDATABASE-nombre de usuarioNombre 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:

bash
# 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.

bash
# 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.js

El 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

ts
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

ts
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

ts
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 SQL representa 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.

ts
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:

ts
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:

ts
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:

ts
// 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

ts
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í:

ts
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.

ts
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.

ts
// 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.

ts
const sql = new SQL({
  hostname: "localhost",
  username: "user",
  password: "password",
  ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});
Modo SSLDescripción
disableNo se usa SSL/TLS. Las conexiones fallan si el servidor requiere SSL.
preferIntenta SSL primero, vuelve a no-SSL si SSL falla. Modo predeterminado si no se especifica ninguno.
requireRequiere SSL sin verificación de certificado. Falla si no se puede establecer SSL.
verify-caVerifica que el certificado del servidor esté firmado por una CA de confianza. Falla si la verificación falla.
verify-fullModo 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:

ts
// 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.

ts
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.

ts
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 inmediatamente

Conexiones 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.

ts
// 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 liberada

Sentencias 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:

ts
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

ts
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ónDescripción
ERR_POSTGRES_CONNECTION_CLOSEDLa conexión fue terminada o nunca se estableció
ERR_POSTGRES_CONNECTION_TIMEOUTFalló al establecer la conexión dentro del tiempo de espera
ERR_POSTGRES_IDLE_TIMEOUTConexión cerrada por inactividad
ERR_POSTGRES_LIFETIME_TIMEOUTLa conexión excedió el tiempo de vida máximo
ERR_POSTGRES_TLS_NOT_AVAILABLEConexión SSL/TLS no disponible
ERR_POSTGRES_TLS_UPGRADE_FAILEDFalló al actualizar la conexión a SSL/TLS

Errores de Autenticación

Errores de AutenticaciónDescripción
ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2Falló la autenticación de contraseña
ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHODEl servidor solicitó método de auth desconocido
ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHODEl servidor solicitó método de auth no soportado
ERR_POSTGRES_INVALID_SERVER_KEYClave de servidor inválida durante autenticación
ERR_POSTGRES_INVALID_SERVER_SIGNATUREFirma de servidor inválida
ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64Codificación de firma SASL inválida
ERR_POSTGRES_SASL_SIGNATURE_MISMATCHFalló la verificación de firma SASL

Errores de Consulta

Errores de ConsultaDescripción
ERR_POSTGRES_SYNTAX_ERRORSintaxis SQL inválida (extiende SyntaxError)
ERR_POSTGRES_SERVER_ERRORError general del servidor PostgreSQL
ERR_POSTGRES_INVALID_QUERY_BINDINGEnlace de parámetros inválido
ERR_POSTGRES_QUERY_CANCELLEDLa consulta fue cancelada
ERR_POSTGRES_NOT_TAGGED_CALLLa consulta se llamó sin una llamada etiquetada

Errores de Tipo de Datos

Errores de Tipo de DatosDescripción
ERR_POSTGRES_INVALID_BINARY_DATAFormato de datos binarios inválido
ERR_POSTGRES_INVALID_BYTE_SEQUENCESecuencia de bytes inválida
ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODINGError de codificación
ERR_POSTGRES_INVALID_CHARACTERCarácter inválido en datos
ERR_POSTGRES_OVERFLOWDesbordamiento numérico
ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMATFormato binario no soportado
ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZETamaño de entero no soportado
ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YETArrays multidimensionales no soportados
ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YETValores NULL en arrays no soportados

Errores de Protocolo

Errores de ProtocoloDescripción
ERR_POSTGRES_EXPECTED_REQUESTSe esperaba solicitud de cliente
ERR_POSTGRES_EXPECTED_STATEMENTSe esperaba sentencia preparada
ERR_POSTGRES_INVALID_BACKEND_KEY_DATADatos de clave de backend inválidos
ERR_POSTGRES_INVALID_MESSAGEMensaje de protocolo inválido
ERR_POSTGRES_INVALID_MESSAGE_LENGTHLongitud de mensaje inválida
ERR_POSTGRES_UNEXPECTED_MESSAGETipo de mensaje inesperado

Errores de Transacción

Errores de TransacciónDescripción
ERR_POSTGRES_UNSAFE_TRANSACTIONSe detectó operación de transacción insegura
ERR_POSTGRES_INVALID_TRANSACTION_STATEEstado 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 ErrorerrnoDescripción
SQLITE_CONSTRAINT19Violación de restricción (UNIQUE, CHECK, NOT NULL, etc.)
SQLITE_BUSY5La base de datos está bloqueada
SQLITE_LOCKED6La tabla en la base de datos está bloqueada
SQLITE_READONLY8Intento de escribir en una base de datos solo lectura
SQLITE_IOERR10Error de I/O de disco
SQLITE_CORRUPT11La imagen de disco de la base de datos está malformada
SQLITE_FULL13La base de datos o disco está lleno
SQLITE_CANTOPEN14No se puede abrir el archivo de base de datos
SQLITE_PROTOCOL15Error de protocolo de bloqueo de base de datos
SQLITE_SCHEMA17El esquema de la base de datos ha cambiado
SQLITE_TOOBIG18La cadena o BLOB excede el límite de tamaño
SQLITE_MISMATCH20Incompatibilidad de tipo de datos
SQLITE_MISUSE21La librería se usó incorrectamente
SQLITE_AUTH23Autorización denegada

Ejemplo de manejo de errores:

ts
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:

ts
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" 12345

BigInt 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:

ts
const sql = new SQL({
  bigint: true,
});

const [{ x }] = await sql`SELECT 9223372036854777 as x`;

console.log(typeof x, x); // "bigint" 9223372036854777n

Hoja de Ruta

Todavía hay algunas cosas que no hemos terminado aún.

  • Precarga de conexiones vía flag --db-preconnect de la CLI de Bun
  • Transformaciones de nombres de columnas (ej. snake_case a camelCase). Esto está mayormente bloqueado en una implementación consciente de unicode para cambiar mayúsculas/minúsculas en C++ usando WTF::String de 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 compatible
  • caching_sha2_password - Predeterminado en MySQL 8.0+, más seguro con intercambio de claves RSA
  • sha256_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:

ts
// 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:

ts
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:

ts
// 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 MySQL

Manejo de Tipos

Los tipos de MySQL se convierten automáticamente a tipos de JavaScript:

Tipo de MySQLTipo de JavaScriptNotas
INT, TINYINT, MEDIUMINTnumberDentro del rango seguro de enteros
BIGINTstring, number o BigIntSi el valor cabe en tamaño i32/u32 será number de lo contrario string o BigInt basado en la opción bigint
DECIMAL, NUMERICstringPara preservar precisión
FLOAT, DOUBLEnumber
DATEDateObjeto Date de JavaScript
DATETIME, TIMESTAMPDateCon manejo de zona horaria
TIMEnumberTotal de microsegundos
YEARnumber
CHAR, VARCHAR, VARSTRING, STRINGstring
TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXTstring
TINY BLOB, MEDIUM BLOB, BLOG, LONG BLOBstringLos tipos BLOB son alias para tipos TEXT
JSONobject/arrayAutomáticamente analizado
BIT(1)booleanBIT(1) en MySQL
GEOMETRYstringDatos de geometría

Diferencias con PostgreSQL

Aunque la API está unificada, hay algunas diferencias de comportamiento:

  1. Marcadores de posición de parámetros: MySQL usa ? internamente pero Bun convierte automáticamente al estilo $1, $2
  2. Cláusula RETURNING: MySQL no soporta RETURNING; usa result.lastInsertRowid o un SELECT separado
  3. 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

ts
// 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

ts
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

  1. Usa agrupación de conexiones: Establece un tamaño de pool max apropiado basado en tu carga de trabajo
  2. Habilita sentencias preparadas: Están habilitadas por defecto y mejoran el rendimiento
  3. Usa transacciones para operaciones masivas: Agrupa consultas relacionadas en transacciones
  4. Indexa apropiadamente: MySQL depende mucho de índices para rendimiento de consultas
  5. 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é:

  1. 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.
  2. 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.

Bun por www.bunjs.com.cn editar