Skip to content

L'interfaccia è progettata per essere semplice e performante, usando template literal con tag per le query e offrendo funzionalità come connection pooling, transazioni e prepared statements.

ts
import { sql, SQL } from "bun";

// PostgreSQL (predefinito)
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}
`;

Caratteristiche

  • Template literal con tag per proteggere contro SQL injection
  • Transazioni
  • Parametri nominali e posizionali
  • Connection pooling
  • Supporto BigInt
  • Supporto autenticazione SASL (SCRAM-SHA-256), MD5 e Clear Text
  • Timeout di connessione
  • Restituzione di righe come oggetti dati, array di array o Buffer
  • Supporto protocollo binario per maggiore velocità
  • Supporto TLS (e modalità auth)
  • Configurazione automatica con variabili d'ambiente

Supporto Database

Bun.SQL fornisce un'API unificata per più sistemi di database:

PostgreSQL

PostgreSQL è usato quando:

  • La stringa di connessione non corrisponde ai pattern SQLite o MySQL (è l'adapter di fallback)
  • La stringa di connessione usa esplicitamente i protocolli postgres:// o postgresql://
  • Non viene fornita alcuna stringa di connessione e le variabili d'ambiente puntano a PostgreSQL
db.ts
ts
import { sql } from "bun";
// Usa PostgreSQL se DATABASE_URL non è impostato o è un URL PostgreSQL
await sql`SELECT ...`;

import { SQL } from "bun";
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
await pg`SELECT ...`;

MySQL

Il supporto MySQL è integrato in Bun.SQL, fornendo la stessa interfaccia con template literal con tag con piena compatibilità per MySQL 5.7+ e MySQL 8.0+:

db.ts
ts
import { SQL } from "bun";

// Connessione MySQL
const mysql = new SQL("mysql://user:password@localhost:3306/database");
const mysql2 = new SQL("mysql2://user:password@localhost:3306/database"); // anche il protocollo mysql2 funziona

// Usando un oggetto opzioni
const mysql3 = new SQL({
  adapter: "mysql",
  hostname: "localhost",
  port: 3306,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",
});

// Funziona con parametri - usa automaticamente prepared statements
const users = await mysql`SELECT * FROM users WHERE id = ${userId}`;

// Le transazioni funzionano come 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}`;
});

// Insert multipli
const newUsers = [
  { name: "Alice", email: "alice@example.com" },
  { name: "Bob", email: "bob@example.com" },
];
await mysql`INSERT INTO users ${mysql(newUsers)}`;

Formati stringhe di connessione MySQL">

MySQL accetta vari formati URL per le stringhe di connessione:

ts
// Protocollo standard mysql://
new SQL("mysql://user:pass@localhost:3306/database");
new SQL("mysql://user:pass@localhost/database"); // Porta predefinita 3306

// Protocollo mysql2:// (compatibilità con pacchetto npm mysql2)
new SQL("mysql2://user:pass@localhost:3306/database");

// Con parametri di query
new SQL("mysql://user:pass@localhost/db?ssl=true");

// Connessione socket Unix
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");

Caratteristiche specifiche di MySQL">

I database MySQL supportano:

  • Prepared statements: Creati automaticamente per query parametrizzate con caching delle statement
  • Protocollo binario: Per migliori prestazioni con prepared statements e gestione accurata dei tipi
  • Set di risultati multipli: Supporto per stored procedure che restituiscono set di risultati multipli
  • Plugin di autenticazione: Supporto per mysql_native_password, caching_sha2_password (predefinito MySQL 8.0) e sha256_password
  • Connessioni SSL/TLS: Modalità SSL configurabili simili a PostgreSQL
  • Attributi di connessione: Informazioni client inviate al server per il monitoraggio
  • Query pipelining: Esegui multiple prepared statements senza attendere le risposte

SQLite

Il supporto SQLite è integrato in Bun.SQL, fornendo la stessa interfaccia con template literal con tag:

ts
import { SQL } from "bun";

// Database in memoria
const memory = new SQL(":memory:");
const memory2 = new SQL("sqlite://:memory:");

// Database su file
const sql1 = new SQL("sqlite://myapp.db");

// Usando un oggetto opzioni
const sql2 = new SQL({
  adapter: "sqlite",
  filename: "./data/app.db",
});

// Per nomi file semplici, specifica l'adapter esplicitamente
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });

Formati stringhe di connessione SQLite">

SQLite accetta vari formati URL per le stringhe di connessione:

ts
// Protocollo standard sqlite://
new SQL("sqlite://path/to/database.db");
new SQL("sqlite:path/to/database.db"); // Senza slash

// Protocollo file:// (anche riconosciuto come SQLite)
new SQL("file://path/to/database.db");
new SQL("file:path/to/database.db");

// Database speciale :memory:
new SQL(":memory:");
new SQL("sqlite://:memory:");
new SQL("file://:memory:");

// Percorsi relativi e assoluti
new SQL("sqlite://./local.db"); // Relativo alla directory corrente
new SQL("sqlite://../parent/db.db"); // Directory superiore
new SQL("sqlite:///absolute/path.db"); // Percorso assoluto

// Con parametri di query
new SQL("sqlite://data.db?mode=ro"); // Modalità sola lettura
new SQL("sqlite://data.db?mode=rw"); // Modalità lettura-scrittura (no creazione)
new SQL("sqlite://data.db?mode=rwc"); // Modalità lettura-scrittura-creazione (predefinita)
<Note>
I nomi file semplici senza protocollo (come `"myapp.db"`) richiedono di specificare esplicitamente `{ adapter: "sqlite" }` per evitare ambiguità con PostgreSQL.
</Note>

Opzioni specifiche di SQLite">

I database SQLite supportano opzioni di configurazione aggiuntive:

ts
const sql = new SQL({
  adapter: "sqlite",
  filename: "app.db",

  // Opzioni specifiche di SQLite
  readonly: false, // Apri in modalità sola lettura
  create: true, // Crea il database se non esiste
  readwrite: true, // Apri per lettura e scrittura

  // Opzioni aggiuntive Bun:sqlite
  strict: true, // Abilita modalità strict
  safeIntegers: false, // Usa numeri JavaScript per gli interi
});

I parametri di query nell'URL vengono analizzati per impostare queste opzioni:

  • ?mode=roreadonly: true
  • ?mode=rwreadonly: false, create: false
  • ?mode=rwcreadonly: false, create: true (predefinito)

Inserimento dati

Puoi passare valori JavaScript direttamente al template literal SQL e l'escaping verrà gestito per te.

ts
import { sql } from "bun";

// Insert base con valori diretti
const [user] = await sql`
  INSERT INTO users (name, email) 
  VALUES (${name}, ${email})
  RETURNING *
`;

// Usando l'helper oggetto per una sintassi più pulita
const userData = {
  name: "Alice",
  email: "alice@example.com",
};

const [newUser] = await sql`
  INSERT INTO users ${sql(userData)}
  RETURNING *
`;
// Si espande a: INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')

Insert multiplo

Puoi anche passare array di oggetti al template literal SQL e verrà espanso in una statement 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)}`;

Scegliere le colonne da inserire

Puoi usare sql(object, ...string) per scegliere quali colonne inserire. Ognuna delle colonne deve essere definita sull'oggetto.

ts
const user = {
  name: "Alice",
  email: "alice@example.com",
  age: 25,
};

await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// Inserisce solo le colonne name e email, ignorando altri campi

Risultati delle Query

Per impostazione predefinita, il client SQL di Bun restituisce i risultati delle query come array di oggetti, dove ogni oggetto rappresenta una riga con i nomi delle colonne come chiavi. Tuttavia, ci sono casi in cui potresti volere i dati in un formato diverso. Il client fornisce due metodi aggiuntivi per questo scopo.

Formato sql``.values()

Il metodo sql``.values() restituisce le righe come array di valori invece che oggetti. Ogni riga diventa un array dove i valori sono nello stesso ordine delle colonne nella tua query.

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

Questo restituisce qualcosa come:

ts
[
  ["Alice", "alice@example.com"],
  ["Bob", "bob@example.com"],
];

sql``.values() è particolarmente utile se nella query vengono restituiti nomi di colonne duplicati. Quando si usano oggetti (predefinito), l'ultimo nome di colonna è usato come chiave nell'oggetto, il che significa che i nomi di colonne duplicati si sovrascrivono a vicenda — ma quando si usa sql``.values(), ogni colonna è presente nell'array quindi puoi accedere ai valori delle colonne duplicate per indice.

Formato sql``.raw()

Il metodo .raw() restituisce le righe come array di oggetti Buffer. Questo può essere utile per lavorare con dati binari o per motivi di prestazioni.

ts
const rows = await sql`SELECT * FROM users`.raw();
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]

Frammenti SQL

Un bisogno comune nelle applicazioni database è la capacità di costruire query dinamicamente in base alle condizioni runtime. Bun fornisce modi sicuri per farlo senza rischiare SQL injection.

Nomi di tabelle dinamici

Quando hai bisogno di riferirti a tabelle o schema dinamicamente, usa l'helper sql() per garantire un escaping corretto:

ts
// Riferisciti in sicurezza alle tabelle dinamicamente
await sql`SELECT * FROM ${sql("users")}`;

// Con qualifica dello schema
await sql`SELECT * FROM ${sql("public.users")}`;

Query condizionali

Puoi usare l'helper sql() per costruire query con clausole condizionali. Questo ti permette di creare query flessibili che si adattano alle esigenze della tua applicazione:

ts
// Clausole WHERE opzionali
const filterAge = true;
const minAge = 21;
const ageFilter = sql`AND age > ${minAge}`;
await sql`
  SELECT * FROM users
  WHERE active = ${true}
  ${filterAge ? ageFilter : sql``}
`;

Colonne dinamiche negli update

Puoi usare sql(object, ...string) per scegliere quali colonne aggiornare. Ognuna delle colonne deve essere definita sull'oggetto. Se le colonne non sono informate tutte le chiavi saranno usate per aggiornare la riga.

ts
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
// usa tutte le chiavi dall'oggetto per aggiornare la riga
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;

Valori dinamici e where in

Anche le liste di valori possono essere create dinamicamente, rendendo semplici anche le query where in. Opzionalmente puoi passare un array di oggetti e informare quale chiave usare per creare 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")}`;

Helper sql.array

L'helper sql.array crea literal di array PostgreSQL da array JavaScript:

ts
// Crea literal di array per PostgreSQL
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
// Genera: INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])

// Funziona anche con array numerici
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` è solo per PostgreSQL. Array multidimensionali e elementi NULL potrebbero non essere ancora supportati.

sql``.simple()

Il protocollo wire di PostgreSQL supporta due tipi di query: "simple" ed "extended". Le query simple possono contenere multiple statement ma non supportano parametri, mentre le query extended (predefinite) supportano parametri ma permettono solo una statement.

Per eseguire multiple statement in una singola query, usa sql``.simple():

ts
// Multiple statement in una query
await sql`
  SELECT 1;
  SELECT 2;
`.simple();

Le query simple sono spesso utili per migrazioni di database e script di setup.

Nota che le query simple non possono usare parametri (${value}). Se hai bisogno di parametri, devi dividere la tua query in statement separate.

Query in file

Puoi usare il metodo sql.file per leggere una query da un file ed eseguirla, se il file include $1, $2, ecc puoi passare parametri alla query. Se non vengono usati parametri può eseguire multiple comandi per file.

ts
const result = await sql.file("query.sql", [1, 2, 3]);

Query unsafe

Puoi usare la funzione sql.unsafe per eseguire stringhe SQL raw. Usa questo con cautela, poiché non eseguirà l'escape dell'input utente. Eseguire più di un comando per query è permesso se non vengono usati parametri.

ts
// Multiple comandi senza parametri
const result = await sql.unsafe(`
  SELECT ${userColumns} FROM users;
  SELECT ${accountColumns} FROM accounts;
`);

// Usando parametri (solo un comando è permesso)
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);

Eseguire e annullare query

Il SQL di Bun è lazy, il che significa che inizierà a eseguire solo quando awaited o eseguito con .execute(). Puoi annullare una query attualmente in esecuzione chiamando il metodo cancel() sull'oggetto query.

ts
const query = sql`SELECT * FROM users`.execute();
setTimeout(() => query.cancel(), 100);
await query;

Variabili d'ambiente Database

I parametri di connessione sql possono essere configurati usando variabili d'ambiente. Il client controlla queste variabili in un ordine specifico di precedenza e rileva automaticamente il tipo di database in base al formato della stringa di connessione.

Rilevamento automatico del database

Quando usi Bun.sql() senza argomenti o new SQL() con una stringa di connessione, l'adapter viene rilevato automaticamente in base al formato URL:

Rilevamento automatico MySQL

MySQL è selezionato automaticamente quando la stringa di connessione corrisponde a questi pattern:

  • mysql://... - URL protocollo MySQL
  • mysql2://... - URL protocollo MySQL2 (alias di compatibilità)
ts
// Questi usano tutti MySQL automaticamente (nessun adapter necessario)
const sql1 = new SQL("mysql://user:pass@localhost/mydb");
const sql2 = new SQL("mysql2://user:pass@localhost:3306/mydb");

// Funziona con la variabile d'ambiente 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

Rilevamento automatico SQLite

SQLite è selezionato automaticamente quando la stringa di connessione corrisponde a questi pattern:

  • :memory: - Database in memoria
  • sqlite://... - URL protocollo SQLite
  • sqlite:... - Protocollo SQLite senza slash
  • file://... - URL protocollo file
  • file:... - Protocollo file senza slash
ts
// Questi usano tutti SQLite automaticamente (nessun adapter necessario)
const sql1 = new SQL(":memory:");
const sql2 = new SQL("sqlite://app.db");
const sql3 = new SQL("file://./database.db");

// Funziona con la variabile d'ambiente 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

Rilevamento automatico PostgreSQL

PostgreSQL è il predefinito per le stringhe di connessione che non corrispondono ai pattern MySQL o SQLite:

bash
# PostgreSQL è rilevato per questi pattern
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 qualsiasi URL che non corrisponde ai pattern MySQL o SQLite
DATABASE_URL="localhost:5432/mydb" bun run app.js

Variabili d'ambiente MySQL

Le connessioni MySQL possono essere configurate tramite variabili d'ambiente:

bash
# URL di connessione primario (controllato per primo)
MYSQL_URL="mysql://user:pass@localhost:3306/mydb"

# Alternativa: DATABASE_URL con protocollo MySQL
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb"

Se non viene fornito alcun URL di connessione, MySQL controlla questi parametri individuali:

Variabile d'ambienteValore predefinitoDescrizione
MYSQL_HOSTlocalhostHost del database
MYSQL_PORT3306Porta del database
MYSQL_USERrootUtente del database
MYSQL_PASSWORD(vuoto)Password del database
MYSQL_DATABASEmysqlNome del database
MYSQL_URL(vuoto)URL di connessione primario MySQL
TLS_MYSQL_DATABASE_URL(vuoto)URL connessione abilitata SSL/TLS

Variabili d'ambiente PostgreSQL

Le seguenti variabili d'ambiente possono essere usate per definire la connessione PostgreSQL:

Variabile d'ambienteDescrizione
POSTGRES_URLURL di connessione primario per PostgreSQL
DATABASE_URLURL di connessione alternativo (rilevato automaticamente)
PGURLURL di connessione alternativo
PG_URLURL di connessione alternativo
TLS_POSTGRES_DATABASE_URLURL connessione abilitata SSL/TLS
TLS_DATABASE_URLURL connessione abilitata SSL/TLS alternativo

Se non viene fornito alcun URL di connessione, il sistema controlla i seguenti parametri individuali:

Variabile d'ambienteVariabili di fallbackValore predefinitoDescrizione
PGHOST-localhostHost del database
PGPORT-5432Porta del database
PGUSERNAMEPGUSER, USER, USERNAMEpostgresUtente del database
PGPASSWORD-(vuoto)Password del database
PGDATABASE-usernameNome del database

Variabili d'ambiente SQLite

Le connessioni SQLite possono essere configurate tramite DATABASE_URL quando contiene un URL compatibile con SQLite:

bash
# Questi sono tutti riconosciuti come SQLite
DATABASE_URL=":memory:"
DATABASE_URL="sqlite://./app.db"
DATABASE_URL="file:///absolute/path/to/db.sqlite"

Nota: Le variabili d'ambiente specifiche di PostgreSQL (POSTGRES_URL, PGHOST, ecc.) vengono ignorate quando si usa SQLite.


Preconnessione Runtime

Bun può preconnettersi a PostgreSQL all'avvio per migliorare le prestazioni stabilendo connessioni al database prima che il codice della tua applicazione venga eseguito. Questo è utile per ridurre la latenza di connessione sulla prima query al database.

bash
# Abilita preconnessione PostgreSQL
bun --sql-preconnect index.js

# Funziona con la variabile d'ambiente DATABASE_URL
DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.js

# Può essere combinato con altri flag runtime
bun --sql-preconnect --hot index.js

Il flag --sql-preconnect stabilirà automaticamente una connessione PostgreSQL usando le tue variabili d'ambiente configurate all'avvio. Se la connessione fallisce, non crasherà la tua applicazione - l'errore verrà gestito in modo elegante.


Opzioni di connessione

Puoi configurare manualmente la tua connessione al database passando opzioni al costruttore SQL. Le opzioni variano a seconda dell'adapter del database:

Opzioni MySQL

ts
import { SQL } from "bun";

const sql = new SQL({
  // Richiesto per MySQL quando si usa l'oggetto opzioni
  adapter: "mysql",

  // Dettagli connessione
  hostname: "localhost",
  port: 3306,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",

  // Connessione socket Unix (alternativa a hostname/port)
  // socket: "/var/run/mysqld/mysqld.sock",

  // Impostazioni connection pool
  max: 20, // Connessioni massime nel pool (predefinito: 10)
  idleTimeout: 30, // Chiudi connessioni inattive dopo 30s
  maxLifetime: 0, // Durata connessione in secondi (0 = per sempre)
  connectionTimeout: 30, // Timeout durante la creazione di nuove connessioni

  // Opzioni 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",
  // },

  // Callback
  onconnect: client => {
    console.log("Connesso a MySQL");
  },
  onclose: (client, err) => {
    if (err) {
      console.error("Errore connessione MySQL:", err);
    } else {
      console.log("Connessione MySQL chiusa");
    }
  },
});

Opzioni PostgreSQL

ts
import { SQL } from "bun";

const sql = new SQL({
  // Dettagli connessione (l'adapter è rilevato automaticamente come PostgreSQL)
  url: "postgres://user:pass@localhost:5432/dbname",

  // Parametri di connessione alternativi
  hostname: "localhost",
  port: 5432,
  database: "myapp",
  username: "dbuser",
  password: "secretpass",

  // Impostazioni connection pool
  max: 20, // Connessioni massime nel pool
  idleTimeout: 30, // Chiudi connessioni inattive dopo 30s
  maxLifetime: 0, // Durata connessione in secondi (0 = per sempre)
  connectionTimeout: 30, // Timeout durante la creazione di nuove connessioni

  // Opzioni 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) {
  //     ...
  //   },
  // },

  // Callback
  onconnect: client => {
    console.log("Connesso a PostgreSQL");
  },
  onclose: client => {
    console.log("Connessione PostgreSQL chiusa");
  },
});

Opzioni SQLite

ts
import { SQL } from "bun";

const sql = new SQL({
  // Richiesto per SQLite
  adapter: "sqlite",
  filename: "./data/app.db", // o ":memory:" per database in memoria

  // Modalità di accesso specifiche di SQLite
  readonly: false, // Apri in modalità sola lettura
  create: true, // Crea il database se non esiste
  readwrite: true, // Consenti operazioni di lettura e scrittura

  // Gestione dati SQLite
  strict: true, // Abilita modalità strict per maggiore sicurezza dei tipi
  safeIntegers: false, // Usa BigInt per interi che superano l'intervallo numeri JS

  // Callback
  onconnect: client => {
    console.log("Database SQLite aperto");
  },
  onclose: client => {
    console.log("Database SQLite chiuso");
  },
});

Note sulla connessione SQLite">

  • Connection Pooling: SQLite non usa connection pooling poiché è un database basato su file. Ogni istanza SQL rappresenta una singola connessione.
  • Transazioni: SQLite supporta transazioni annidate tramite savepoint, simile a PostgreSQL.
  • Accesso concorrente: SQLite gestisce l'accesso concorrente tramite locking dei file. Usa la modalità WAL per una migliore concorrenza.
  • Database in memoria: Usare :memory: crea un database temporaneo che esiste solo per la durata della connessione.

Password dinamiche

Quando i client hanno bisogno di usare schemi di autenticazione alternativi come token di accesso o connessioni a database con password rotanti, fornisci una funzione sincrona o asincrona che risolverà il valore della password dinamica al momento della connessione.

ts
import { SQL } from "bun";

const sql = new SQL(url, {
  // Altra configurazione connessione
  ...
  // Funzione password per l'utente del database
  password: async () => await signer.getAuthToken(),
});

Caratteristiche specifiche di SQLite

Esecuzione query

SQLite esegue le query in modo sincrono, a differenza di PostgreSQL che usa I/O asincrono. Tuttavia, l'API rimane coerente usando Promise:

ts
const sqlite = new SQL("sqlite://app.db");

// Funziona come PostgreSQL, ma esegue in modo sincrono sotto il cofano
const users = await sqlite`SELECT * FROM users`;

// I parametri funzionano in modo identico
const user = await sqlite`SELECT * FROM users WHERE id = ${userId}`;

Pragmi SQLite

Puoi usare statement PRAGMA per configurare il comportamento di SQLite:

ts
const sqlite = new SQL("sqlite://app.db");

// Abilita chiavi esterne
await sqlite`PRAGMA foreign_keys = ON`;

// Imposta la modalità journal su WAL per una migliore concorrenza
await sqlite`PRAGMA journal_mode = WAL`;

// Controlla l'integrità
const integrity = await sqlite`PRAGMA integrity_check`;

Differenze nei tipi di dati

SQLite ha un sistema di tipi più flessibile di PostgreSQL:

ts
// SQLite memorizza i dati in 5 classi di archiviazione: NULL, INTEGER, REAL, TEXT, BLOB
const sqlite = new SQL("sqlite://app.db");

// SQLite è più flessibile con i tipi
await sqlite`
  CREATE TABLE flexible (
    id INTEGER PRIMARY KEY,
    data TEXT,        -- Può memorizzare numeri come stringhe
    value NUMERIC,    -- Può memorizzare interi, reali o testo
    blob BLOB         -- Dati binari
  )
`;

// I valori JavaScript vengono convertiti automaticamente
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;

Transazioni

Per avviare una nuova transazione, usa sql.begin. Questo metodo funziona sia per PostgreSQL che per SQLite. Per PostgreSQL, riserva una connessione dedicata dal pool. Per SQLite, inizia una transazione sulla singola connessione.

Il comando BEGIN viene inviato automaticamente, incluse eventuali configurazioni opzionali che specifichi. Se si verifica un errore durante la transazione, viene attivato un ROLLBACK per garantire che il processo continui senza problemi.

Transazioni base

ts
await sql.begin(async tx => {
  // Tutte le query in questa funzione vengono eseguite in una transazione
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
  await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`;

  // La transazione viene automaticamente commessa se non vengono lanciati errori
  // Rollback se si verifica un errore
});

È anche possibile pipeline le richieste in una transazione se necessario restituendo un array con le query dalla funzione callback così:

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`,
  ];
});

Savepoint

I savepoint in SQL creano checkpoint intermedi all'interno di una transazione, consentendo rollback parziali senza influenzare l'intera operazione. Sono utili in transazioni complesse, consentendo il recupero dagli errori e mantenendo risultati coerenti.

ts
await sql.begin(async tx => {
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;

  await tx.savepoint(async sp => {
    // Questa parte può essere sottoposta a rollback separatamente
    await sp`UPDATE users SET status = 'active'`;
    if (someCondition) {
      throw new Error("Rollback al savepoint");
    }
  });

  // Continua con la transazione anche se il savepoint è stato sottoposto a rollback
  await tx`INSERT INTO audit_log (action) VALUES ('user_created')`;
});

Transazioni distribuite

Two-Phase Commit (2PC) è un protocollo di transazione distribuita dove la Fase 1 ha il coordinatore che prepara i nodi assicurando che i dati siano scritti e pronti per il commit, mentre la Fase 2 finalizza con i nodi che eseguono il commit o il rollback in base alla decisione del coordinatore. Questo processo garantisce la durabilità dei dati e una corretta gestione dei lock.

In PostgreSQL e MySQL, le transazioni distribuite persistono oltre la loro sessione originale, consentendo a utenti privilegiati o coordinatori di eseguire il commit o il rollback successivamente. Questo supporta transazioni distribuite robuste, processi di recupero e operazioni amministrative.

Ogni sistema di database implementa le transazioni distribuite in modo diverso:

PostgreSQL le supporta nativamente tramite transazioni preparate, mentre MySQL usa le transazioni XA.

Se si verificano eccezioni durante la transazione distribuita e non vengono catturate, il sistema eseguirà automaticamente il rollback di tutte le modifiche. Quando tutto procede normalmente, mantieni la flessibilità di eseguire il commit o il rollback della transazione successivamente.

ts
// Inizia una transazione distribuita
await sql.beginDistributed("tx1", async tx => {
  await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
});

// Successivamente, commit o rollback
await sql.commitDistributed("tx1");
// oppure
await sql.rollbackDistributed("tx1");

Autenticazione

Bun supporta l'autenticazione SCRAM-SHA-256 (SASL), MD5 e Clear Text. SASL è raccomandato per una migliore sicurezza. Consulta Autenticazione SASL di Postgres per maggiori informazioni.

Panoramica modalità SSL

PostgreSQL supporta diverse modalità SSL/TLS per controllare come vengono stabilite le connessioni sicure. Queste modalità determinano il comportamento durante la connessione e il livello di verifica del certificato eseguito.

ts
const sql = new SQL({
  hostname: "localhost",
  username: "user",
  password: "password",
  ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});
Modalità SSLDescrizione
disableNessun SSL/TLS usato. Le connessioni falliscono se il server richiede SSL.
preferProva prima SSL, ripiega su non-SSL se SSL fallisce. Modalità predefinita se non specificata.
requireRichiede SSL senza verifica del certificato. Fallisce se SSL non può essere stabilito.
verify-caVerifica che il certificato del server sia firmato da una CA attendibile. Fallisce se la verifica fallisce.
verify-fullModalità più sicura. Verifica certificato e corrispondenza hostname. Protegge contro certificati non attendibili e attacchi MITM.

Uso con stringhe di connessione

La modalità SSL può anche essere specificata nelle stringhe di connessione:

ts
// Usando la modalità prefer
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");

// Usando la modalità verify-full
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");

Connection Pooling

Il client SQL di Bun gestisce automaticamente un connection pool, che è un pool di connessioni al database riutilizzate per multiple query. Questo aiuta a ridurre l'overhead di stabilire e chiudere connessioni per ogni query e aiuta anche a gestire il numero di connessioni concorrenti al database.

ts
const sql = new SQL({
  // Configurazione pool
  max: 20, // Massimo 20 connessioni concorrenti
  idleTimeout: 30, // Chiudi connessioni inattive dopo 30s
  maxLifetime: 3600, // Durata massima connessione 1 ora
  connectionTimeout: 10, // Timeout connessione 10s
});

Nessuna connessione verrà fatta finché non viene fatta una query.

ts
const sql = Bun.SQL(); // nessuna connessione viene creata

await sql`...`; // il pool viene avviato fino a max raggiunto (se possibile), viene usata la prima connessione disponibile
await sql`...`; // la connessione precedente viene riutilizzata

// due connessioni sono usate ora allo stesso tempo
await Promise.all([
  sql`INSERT INTO users ${sql({ name: "Alice" })}`,
  sql`UPDATE users SET name = ${user.name} WHERE id = ${user.id}`,
]);

await sql.close(); // attendi che tutte le query finiscano e chiudi tutte le connessioni dal pool
await sql.close({ timeout: 5 }); // attendi 5 secondi e chiudi tutte le connessioni dal pool
await sql.close({ timeout: 0 }); // chiudi tutte le connessioni dal pool immediatamente

Connessioni riservate

Bun ti permette di riservare una connessione dal pool e restituisce un client che incapsula la singola connessione. Questo può essere usato per eseguire query su una connessione isolata.

ts
// Ottieni connessione esclusiva dal pool
const reserved = await sql.reserve();

try {
  await reserved`INSERT INTO users (name) VALUES (${"Alice"})`;
} finally {
  // Importante: Rilascia connessione al pool
  reserved.release();
}

// O usando Symbol.dispose
{
  using reserved = await sql.reserve();
  await reserved`SELECT 1`;
} // Rilasciato automaticamente

Prepared Statements

Per impostazione predefinita, il client SQL di Bun crea automaticamente prepared statement nominati per le query dove si può dedurre che la query è statica. Questo fornisce migliori prestazioni. Tuttavia, puoi cambiare questo comportamento impostando prepare: false nelle opzioni di connessione:

ts
const sql = new SQL({
  // ... altre opzioni ...
  prepare: false, // Disabilita persistenza prepared statement nominati sul server
});

Quando prepare: false è impostato:

Le query vengono ancora eseguite usando il protocollo "extended", ma vengono eseguite usando prepared statement senza nome, un prepared statement senza nome dura solo finché non viene emessa la prossima statement Parse che specifica la statement senza nome come destinazione.

  • Il binding dei parametri è comunque sicuro contro SQL injection
  • Ogni query viene analizzata e pianificata da zero dal server
  • Le query non verranno pipelined

Potresti voler usare prepare: false quando:

  • Usi PGBouncer in modalità transazione (anche se dalla versione 1.21.0 di PGBouncer, i prepared statement a livello di protocollo sono supportati se configurati correttamente)
  • Debug di piani di esecuzione query
  • Lavori con SQL dinamico dove i piani di query devono essere rigenerati frequentemente
  • Più di un comando per query non sarà supportato (a meno che non usi sql``.simple())

Nota che disabilitare i prepared statement potrebbe influire sulle prestazioni per le query eseguite frequentemente con parametri diversi, poiché il server deve analizzare e pianificare ogni query da zero.


Gestione errori

Il client fornisce errori tipizzati per diversi scenari di fallimento. Gli errori sono specifici del database ed estendono classi di errori base:

Classi di errore

ts
import { SQL } from "bun";

try {
  await sql`SELECT * FROM users`;
} catch (error) {
  if (error instanceof SQL.PostgresError) {
    // Errore specifico di PostgreSQL
    console.log(error.code); // Codice errore PostgreSQL
    console.log(error.detail); // Messaggio di errore dettagliato
    console.log(error.hint); // Suggerimento utile da PostgreSQL
  } else if (error instanceof SQL.SQLiteError) {
    // Errore specifico di SQLite
    console.log(error.code); // Codice errore SQLite (es. "SQLITE_CONSTRAINT")
    console.log(error.errno); // Numero errore SQLite
    console.log(error.byteOffset); // Offset byte nella statement SQL (se disponibile)
  } else if (error instanceof SQL.SQLError) {
    // Errore SQL generico (classe base)
    console.log(error.message);
  }
}

Codici di errore specifici di PostgreSQL">

Errori di connessione PostgreSQL

Errori di connessioneDescrizione
ERR_POSTGRES_CONNECTION_CLOSEDLa connessione è stata terminata o non è mai stata stabilita
ERR_POSTGRES_CONNECTION_TIMEOUTFallimento stabilimento connessione entro il timeout
ERR_POSTGRES_IDLE_TIMEOUTConnessione chiusa per inattività
ERR_POSTGRES_LIFETIME_TIMEOUTConnessione ha superato la durata massima
ERR_POSTGRES_TLS_NOT_AVAILABLEConnessione SSL/TLS non disponibile
ERR_POSTGRES_TLS_UPGRADE_FAILEDFallimento upgrade connessione a SSL/TLS

Errori di autenticazione

Errori di autenticazioneDescrizione
ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2Autenticazione password fallita
ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHODIl server ha richiesto metodo auth sconosciuto
ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHODIl server ha richiesto metodo auth non supportato
ERR_POSTGRES_INVALID_SERVER_KEYChiave server non valida durante autenticazione
ERR_POSTGRES_INVALID_SERVER_SIGNATUREFirma server non valida
ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64Codifica firma SASL non valida
ERR_POSTGRES_SASL_SIGNATURE_MISMATCHVerifica firma SASL fallita

Errori di query

Errori di queryDescrizione
ERR_POSTGRES_SYNTAX_ERRORSintassi SQL non valida (estende SyntaxError)
ERR_POSTGRES_SERVER_ERRORErrore generale dal server PostgreSQL
ERR_POSTGRES_INVALID_QUERY_BINDINGBinding parametri non valido
ERR_POSTGRES_QUERY_CANCELLEDQuery è stata annullata
ERR_POSTGRES_NOT_TAGGED_CALLQuery è stata chiamata senza tagged call

Errori di tipo di dati

Errori di tipo di datiDescrizione
ERR_POSTGRES_INVALID_BINARY_DATAFormato dati binari non valido
ERR_POSTGRES_INVALID_BYTE_SEQUENCESequenza byte non valida
ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODINGErrore di codifica
ERR_POSTGRES_INVALID_CHARACTERCarattere non valido nei dati
ERR_POSTGRES_OVERFLOWOverflow numerico
ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMATFormato binario non supportato
ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZEDimensione intero non supportata
ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YETArray multidimensionali non supportati
ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YETValori NULL negli array non supportati

Errori di protocollo

Errori di protocolloDescrizione
ERR_POSTGRES_EXPECTED_REQUESTRichiesta client attesa
ERR_POSTGRES_EXPECTED_STATEMENTPrepared statement atteso
ERR_POSTGRES_INVALID_BACKEND_KEY_DATADati chiave backend non validi
ERR_POSTGRES_INVALID_MESSAGEMessaggio protocollo non valido
ERR_POSTGRES_INVALID_MESSAGE_LENGTHLunghezza messaggio non valida
ERR_POSTGRES_UNEXPECTED_MESSAGETipo messaggio inatteso

Errori di transazione

Errori di transazioneDescrizione
ERR_POSTGRES_UNSAFE_TRANSACTIONOperazione transazione non sicura rilevata
ERR_POSTGRES_INVALID_TRANSACTION_STATEStato transazione non valido

Errori specifici di SQLite

Gli errori SQLite forniscono codici di errore e numeri che corrispondono ai codici di errore standard di SQLite:

Codici di errore SQLite comuni">

Codice erroreerrnoDescrizione
SQLITE_CONSTRAINT19Violazione vincolo (UNIQUE, CHECK, NOT NULL, ecc.)
SQLITE_BUSY5Database è bloccato
SQLITE_LOCKED6Tabella nel database è bloccata
SQLITE_READONLY8Tentativo di scrittura su database di sola lettura
SQLITE_IOERR10Errore I/O disco
SQLITE_CORRUPT11Immagine disco database è danneggiata
SQLITE_FULL13Database o disco è pieno
SQLITE_CANTOPEN14Impossibile aprire il file del database
SQLITE_PROTOCOL15Errore protocollo lock database
SQLITE_SCHEMA17Schema del database è cambiato
SQLITE_TOOBIG18Stringa o BLOB supera il limite dimensione
SQLITE_MISMATCH20Mancata corrispondenza tipo di dati
SQLITE_MISUSE21Libreria usata in modo errato
SQLITE_AUTH23Autorizzazione negata

Esempio gestione errori:

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 duplicato
} catch (error) {
  if (error instanceof SQL.SQLiteError) {
    if (error.code === "SQLITE_CONSTRAINT") {
      console.log("Violazione vincolo:", error.message);
      // Gestisci violazione vincolo unique
    }
  }
}

Numeri e BigInt

Il client SQL di Bun include una gestione speciale per numeri grandi che superano l'intervallo di un intero a 53 bit. Ecco come funziona:

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 invece di stringhe

Se hai bisogno di numeri grandi come BigInt invece di stringhe, puoi abilitarlo impostando l'opzione bigint su true quando inizializzi il client SQL:

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

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

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

Roadmap

Ci sono ancora alcune cose che non abbiamo finito.

  • Precaricamento connessioni tramite flag CLI --db-preconnect
  • Trasformazioni nomi colonne (es. snake_case a camelCase). Questo è per lo più bloccato su un'implementazione unicode-aware per cambiare il case in C++ usando WTF::String di WebKit.
  • Trasformazioni tipi di colonne

Caratteristiche specifiche del database

Metodi di autenticazione

MySQL supporta multiple plugin di autenticazione che vengono negoziati automaticamente:

  • mysql_native_password - Autenticazione MySQL tradizionale, ampiamente compatibile
  • caching_sha2_password - Predefinito in MySQL 8.0+, più sicuro con scambio chiavi RSA
  • sha256_password - Autenticazione basata su SHA-256

Il client gestisce automaticamente il cambio plugin di autenticazione quando richiesto dal server, incluso lo scambio sicuro di password su connessioni non-SSL.

Prepared Statements e prestazioni

MySQL usa prepared statement lato server per tutte le query parametrizzate:

ts
// Questo crea automaticamente un prepared statement sul server
const user = await mysql`SELECT * FROM users WHERE id = ${userId}`;

// I prepared statement vengono cached e riutilizzati per query identiche
for (const id of userIds) {
  // Lo stesso prepared statement viene riutilizzato
  await mysql`SELECT * FROM users WHERE id = ${id}`;
}

// Query pipelining - multiple statement inviate senza attendere risposte
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}`,
]);

Set di risultati multipli

MySQL può restituire set di risultati multipli da query multi-statement:

ts
const mysql = new SQL("mysql://user:pass@localhost/mydb");

// Query multi-statement con metodo simple()
const multiResults = await mysql`
  SELECT * FROM users WHERE id = 1;
  SELECT * FROM orders WHERE user_id = 1;
`.simple();

Set di caratteri e collazioni

Bun.SQL usa automaticamente il set di caratteri utf8mb4 per le connessioni MySQL, garantendo pieno supporto Unicode inclusi emoji. Questo è il set di caratteri raccomandato per applicazioni MySQL moderne.

Attributi di connessione

Bun invia automaticamente informazioni client a MySQL per un migliore monitoraggio:

ts
// Questi attributi vengono inviati automaticamente:
// _client_name: "Bun"
// _client_version: <versione bun>
// Puoi vederli in performance_schema.session_connect_attrs di MySQL

Gestione tipi

I tipi MySQL vengono automaticamente convertiti in tipi JavaScript:

Tipo MySQLTipo JavaScriptNote
INT, TINYINT, MEDIUMINTnumberEntro intervallo integer sicuro
BIGINTstring, number o BigIntSe il valore rientra in i32/u32 sarà number altrimenti string o BigInt in base all'opzione bigint
DECIMAL, NUMERICstringPer preservare la precisione
FLOAT, DOUBLEnumber
DATEDateOggetto Date JavaScript
DATETIME, TIMESTAMPDateCon gestione fuso orario
TIMEnumberTotale di microsecondi
YEARnumber
CHAR, VARCHAR, VARSTRING, STRINGstring
TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXTstring
TINY BLOB, MEDIUM BLOB, BLOG, LONG BLOBstringI tipi BLOB sono alias per i tipi TEXT
JSONobject/arrayAnalizzato automaticamente
BIT(1)booleanBIT(1) in MySQL
GEOMETRYstringDati geometria

Differenze da PostgreSQL

Sebbene l'API sia unificata, ci sono alcune differenze comportamentali:

  1. Placeholder parametri: MySQL usa ? internamente ma Bun converte automaticamente in stile $1, $2
  2. Clausola RETURNING: MySQL non supporta RETURNING; usa result.lastInsertRowid o un SELECT separato
  3. Tipi array: MySQL non ha tipi array nativi come PostgreSQL

Caratteristiche specifiche di MySQL

Non abbiamo ancora implementato il supporto per LOAD DATA INFILE

Caratteristiche specifiche di PostgreSQL

Non abbiamo ancora implementato:

  • Supporto COPY
  • Supporto LISTEN
  • Supporto NOTIFY

Non abbiamo anche implementato alcune delle funzionalità più uncommon come:

  • Autenticazione GSSAPI
  • Supporto SCRAM-SHA-256-PLUS
  • Tipi Point e PostGIS
  • Tutti i tipi di array integer multidimensionali (solo alcuni tipi sono supportati)

Pattern comuni e best practice

Lavorare con set di risultati MySQL

ts
// Ottenere ID insert dopo INSERT
const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
console.log(result.lastInsertRowid); // LAST_INSERT_ID() di MySQL

// Gestire righe interessate
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
console.log(updated.affectedRows); // Numero di righe aggiornate

// Usare funzioni specifiche di MySQL
const now = await mysql`SELECT NOW() as current_time`;
const uuid = await mysql`SELECT UUID() as id`;

Gestione errori MySQL

ts
try {
  await mysql`INSERT INTO users (email) VALUES (${"duplicate@email.com"})`;
} catch (error) {
  if (error.code === "ER_DUP_ENTRY") {
    console.log("Voce duplicata rilevata");
  } else if (error.code === "ER_ACCESS_DENIED_ERROR") {
    console.log("Accesso negato");
  } else if (error.code === "ER_BAD_DB_ERROR") {
    console.log("Il database non esiste");
  }
  // I codici errore MySQL sono compatibili con i pacchetti mysql/mysql2
}

Consigli prestazioni per MySQL

  1. Usa connection pooling: Imposta dimensione pool max appropriata in base al tuo carico di lavoro
  2. Abilita prepared statements: Sono abilitati di default e migliorano le prestazioni
  3. Usa transazioni per operazioni bulk: Raggruppa query correlate in transazioni
  4. Indicizza correttamente: MySQL si affida molto agli indici per le prestazioni delle query
  5. Usa set di caratteri utf8mb4: È impostato di default e gestisce tutti i caratteri Unicode

Domande frequenti

Perché questo è `Bun.sql` e non `Bun.postgres`?
	Il piano era aggiungere più driver di database in futuro. Ora con il supporto MySQL aggiunto, questa API unificata supporta PostgreSQL, MySQL e SQLite.
Come faccio a sapere quale adapter database viene usato?
	L'adapter viene rilevato automaticamente dalla stringa di connessione:

	- URL che iniziano con `mysql://` o `mysql2://` usano MySQL
	- URL che corrispondono ai pattern SQLite (`:memory:`, `sqlite://`, `file://`) usano SQLite
	- Tutto il resto usa PostgreSQL di default

	Sono supportate le stored procedure MySQL?">
	Sì, le stored procedure sono pienamente supportate inclusi parametri OUT e set di risultati multipli:

	```ts
	// Chiama stored procedure
	const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;

	// Ottieni parametro OUT
	const outParam = await mysql`SELECT @total_orders as total`;
	```

	Posso usare sintassi SQL specifica di MySQL?">
	Sì, puoi usare qualsiasi sintassi specifica di MySQL:

	```ts
	// La sintassi specifica di MySQL funziona bene
	await mysql`SET @user_id = ${userId}`;
	await mysql`SHOW TABLES`;
	await mysql`DESCRIBE users`;
	await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
	```

Perché non usare semplicemente una libreria esistente?

Pacchetti npm come postgres.js, pg e node-postgres possono essere usati anche in Bun. Sono ottime opzioni.

Due motivi:

  1. Pensiamo che sia più semplice per gli sviluppatori avere un driver di database integrato in Bun. Il tempo che passi a cercare librerie è tempo che potresti usare per costruire la tua app.
  2. Sfruttiamo alcuni interni del motore JavaScriptCore per rendere più veloce creare oggetti che sarebbero difficili da implementare in una libreria

Ringraziamenti

Un enorme ringraziamento a @porsager per postgres.js per l'ispirazione per l'interfaccia API.

Bun a cura di www.bunjs.com.cn