Die Schnittstelle ist einfach und performant gestaltet, verwendet getaggte Template-Literale für Abfragen und bietet Funktionen wie Connection Pooling, Transaktionen und vorbereitete Anweisungen.
import { sql, SQL } from "bun";
// PostgreSQL (Standard)
const users = await sql`
SELECT * FROM users
WHERE active = ${true}
LIMIT ${10}
`;
// Mit MySQL
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb");
const mysqlResults = await mysql`
SELECT * FROM users
WHERE active = ${true}
`;
// Mit SQLite
const sqlite = new SQL("sqlite://myapp.db");
const sqliteResults = await sqlite`
SELECT * FROM users
WHERE active = ${1}
`;Funktionen
- Getaggte Template-Literale zum Schutz vor SQL-Injection
- Transaktionen
- Benannte und positionale Parameter
- Connection Pooling
BigInt-Unterstützung- SASL-Auth-Unterstützung (SCRAM-SHA-256), MD5 und Klartext
- Connection-Timeouts
- Rückgabe von Zeilen als Datenobjekte, Arrays von Arrays oder Buffer
- Binary-Protocol-Unterstützung macht es schneller
- TLS-Unterstützung (und Auth-Modus)
- Automatische Konfiguration mit Umgebungsvariable
Datenbankunterstützung
Bun.SQL bietet eine einheitliche API für mehrere Datenbanksysteme:
PostgreSQL
PostgreSQL wird verwendet, wenn:
- Die Verbindungszeichenfolge nicht mit SQLite- oder MySQL-Mustern übereinstimmt (es ist der Fallback-Adapter)
- Die Verbindungszeichenfolge explizit die Protokolle
postgres://oderpostgresql://verwendet - Keine Verbindungszeichenfolge angegeben ist und Umgebungsvariablen auf PostgreSQL verweisen
import { sql } from "bun";
// Verwendet PostgreSQL, wenn DATABASE_URL nicht gesetzt ist oder eine PostgreSQL-URL ist
await sql`SELECT ...`;
import { SQL } from "bun";
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
await pg`SELECT ...`;MySQL
MySQL-Unterstützung ist in Bun.SQL integriert und bietet die gleiche Schnittstelle für getaggte Template-Literale mit vollständiger Kompatibilität für MySQL 5.7+ und MySQL 8.0+:
import { SQL } from "bun";
// MySQL-Verbindung
const mysql = new SQL("mysql://user:password@localhost:3306/database");
const mysql2 = new SQL("mysql2://user:password@localhost:3306/database"); // mysql2-Protokoll funktioniert ebenfalls
// Verwenden eines Optionsobjekts
const mysql3 = new SQL({
adapter: "mysql",
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
});
// Funktioniert mit Parametern - verwendet automatisch vorbereitete Anweisungen
const users = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Transaktionen funktionieren wie bei 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}`;
});
// Masseneinfügungen
const newUsers = [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
];
await mysql`INSERT INTO users ${mysql(newUsers)}`;MySQL-Verbindungszeichenfolgenformate">
MySQL akzeptiert verschiedene URL-Formate für Verbindungszeichenfolgen:
// Standard mysql://-Protokoll
new SQL("mysql://user:pass@localhost:3306/database");
new SQL("mysql://user:pass@localhost/database"); // Standard-Port 3306
// mysql2://-Protokoll (Kompatibilität mit mysql2 npm-Paket)
new SQL("mysql2://user:pass@localhost:3306/database");
// Mit Abfrageparametern
new SQL("mysql://user:pass@localhost/db?ssl=true");
// Unix-Socket-Verbindung
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");MySQL-spezifische Funktionen">
MySQL-Datenbanken unterstützen:
- Vorbereitete Anweisungen: Automatisch erstellt für parametrisierte Abfragen mit Anweisungs-Caching
- Binary-Protokoll: Für bessere Performance mit vorbereiteten Anweisungen und genauer Typbehandlung
- Mehrere Ergebnissätze: Unterstützung für gespeicherte Prozeduren, die mehrere Ergebnissätze zurückgeben
- Authentifizierungs-Plugins: Unterstützung für mysql_native_password, caching_sha2_password (MySQL 8.0-Standard) und sha256_password
- SSL/TLS-Verbindungen: Konfigurierbare SSL-Modi ähnlich wie bei PostgreSQL
- Verbindungsattribute: Client-Informationen, die zur Überwachung an den Server gesendet werden
- Abfrage-Pipelining: Mehrere vorbereitete Anweisungen ohne Warten auf Antworten ausführen
SQLite
SQLite-Unterstützung ist in Bun.SQL integriert und bietet die gleiche Schnittstelle für getaggte Template-Literale:
import { SQL } from "bun";
// In-Memory-Datenbank
const memory = new SQL(":memory:");
const memory2 = new SQL("sqlite://:memory:");
// Dateibasierte Datenbank
const sql1 = new SQL("sqlite://myapp.db");
// Verwenden eines Optionsobjekts
const sql2 = new SQL({
adapter: "sqlite",
filename: "./data/app.db",
});
// Für einfache Dateinamen Adapter explizit angeben
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });SQLite-Verbindungszeichenfolgenformate">
SQLite akzeptiert verschiedene URL-Formate für Verbindungszeichenfolgen:
// Standard sqlite://-Protokoll
new SQL("sqlite://path/to/database.db");
new SQL("sqlite:path/to/database.db"); // Ohne Schrägstriche
// file://-Protokoll (wird ebenfalls als SQLite erkannt)
new SQL("file://path/to/database.db");
new SQL("file:path/to/database.db");
// Spezielle :memory:-Datenbank
new SQL(":memory:");
new SQL("sqlite://:memory:");
new SQL("file://:memory:");
// Relative und absolute Pfade
new SQL("sqlite://./local.db"); // Relativ zum aktuellen Verzeichnis
new SQL("sqlite://../parent/db.db"); // Übergeordnetes Verzeichnis
new SQL("sqlite:///absolute/path.db"); // Absoluter Pfad
// Mit Abfrageparametern
new SQL("sqlite://data.db?mode=ro"); // Nur-Lese-Modus
new SQL("sqlite://data.db?mode=rw"); // Lese-Schreib-Modus (kein Erstellen)
new SQL("sqlite://data.db?mode=rwc"); // Lese-Schreib-Erstellen-Modus (Standard)<Note>
Einfache Dateinamen ohne Protokoll (wie `"myapp.db"`) erfordern die explizite Angabe von `{ adapter: "sqlite" }`, um Mehrdeutigkeiten mit PostgreSQL zu vermeiden.
</Note>
SQLite-spezifische Optionen">
SQLite-Datenbanken unterstützen zusätzliche Konfigurationsoptionen:
const sql = new SQL({
adapter: "sqlite",
filename: "app.db",
// SQLite-spezifische Optionen
readonly: false, // Im Nur-Lese-Modus öffnen
create: true, // Datenbank erstellen, falls nicht vorhanden
readwrite: true, // Zum Lesen und Schreiben öffnen
// Zusätzliche Bun:sqlite-Optionen
strict: true, // Strengen Modus aktivieren
safeIntegers: false, // JavaScript-Zahlen für Ganzzahlen verwenden
});Abfrageparameter in der URL werden geparst, um diese Optionen festzulegen:
?mode=ro→readonly: true?mode=rw→readonly: false, create: false?mode=rwc→readonly: false, create: true(Standard)
Daten einfügen
Sie können JavaScript-Werte direkt an das SQL-Template-Literal übergeben, und die Maskierung wird für Sie handled.
import { sql } from "bun";
// Grundlegendes Einfügen mit direkten Werten
const [user] = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING *
`;
// Objekthelfer für sauberere Syntax verwenden
const userData = {
name: "Alice",
email: "alice@example.com",
};
const [newUser] = await sql`
INSERT INTO users ${sql(userData)}
RETURNING *
`;
// Erweitert zu: INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')Masseneinfügung
Sie können auch Arrays von Objekten an das SQL-Template-Literal übergeben, und es wird zu einer INSERT INTO ... VALUES ...-Anweisung erweitert.
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)}`;Spalten zum Einfügen auswählen
Sie können sql(object, ...string) verwenden, um auszuwählen, welche Spalten eingefügt werden sollen. Jede der Spalten muss im Objekt definiert sein.
const user = {
name: "Alice",
email: "alice@example.com",
age: 25,
};
await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// Fügt nur name- und email-Spalten ein und ignoriert andere FelderAbfrageergebnisse
Standardmäßig gibt Buns SQL-Client Abfrageergebnisse als Arrays von Objekten zurück, wobei jedes Objekt eine Zeile mit Spaltennamen als Schlüssel darstellt. Es gibt jedoch Fälle, in denen Sie die Daten in einem anderen Format haben möchten. Der Client bietet zwei zusätzliche Methoden für diesen Zweck.
sql``.values()-Format
Die sql``.values()-Methode gibt Zeilen als Arrays von Werten zurück, anstatt als Objekte. Jede Zeile wird zu einem Array, wobei die Werte in der gleichen Reihenfolge wie die Spalten in Ihrer Abfrage sind.
const rows = await sql`SELECT * FROM users`.values();
console.log(rows);Dies gibt so etwas zurück wie:
[
["Alice", "alice@example.com"],
["Bob", "bob@example.com"],
];sql``.values() ist besonders nützlich, wenn doppelte Spaltennamen in den Abfrageergebnissen zurückgegeben werden. Bei Verwendung von Objekten (Standard) wird der letzte Spaltenname als Schlüssel im Objekt verwendet, was bedeutet, dass doppelte Spaltennamen sich gegenseitig überschreiben — aber bei Verwendung von sql``.values() ist jede Spalte im Array vorhanden, sodass Sie auf die Werte doppelter Spalten per Index zugreifen können.
sql``.raw()-Format
Die .raw()-Methode gibt Zeilen als Arrays von Buffer-Objekten zurück. Dies kann für die Arbeit mit Binärdaten oder aus Performance-Gründen nützlich sein.
const rows = await sql`SELECT * FROM users`.raw();
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]SQL-Fragmente
Ein häufiges Bedürfnis in Datenbankanwendungen ist die Möglichkeit, Abfragen dynamisch basierend auf Laufzeitbedingungen zu konstruieren. Bun bietet sichere Möglichkeiten, dies zu tun, ohne das Risiko einer SQL-Injection.
Dynamische Tabellennamen
Wenn Sie Tabellen oder Schemata dynamisch referenzieren müssen, verwenden Sie den sql()-Helfer, um eine ordnungsgemäße Maskierung sicherzustellen:
// Tabellen sicher dynamisch referenzieren
await sql`SELECT * FROM ${sql("users")}`;
// Mit Schema-Qualifikation
await sql`SELECT * FROM ${sql("public.users")}`;Bedingte Abfragen
Sie können den sql()-Helfer verwenden, um Abfragen mit bedingten Klauseln zu erstellen. Dies ermöglicht es Ihnen, flexible Abfragen zu erstellen, die sich an die Bedürfnisse Ihrer Anwendung anpassen:
// Optionale WHERE-Klauseln
const filterAge = true;
const minAge = 21;
const ageFilter = sql`AND age > ${minAge}`;
await sql`
SELECT * FROM users
WHERE active = ${true}
${filterAge ? ageFilter : sql``}
`;Dynamische Spalten in Updates
Sie können sql(object, ...string) verwenden, um auszuwählen, welche Spalten aktualisiert werden sollen. Jede der Spalten muss im Objekt definiert sein. Wenn die Spalten nicht angegeben werden, werden alle Schlüssel zum Aktualisieren der Zeile verwendet.
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
// Verwendet alle Schlüssel aus dem Objekt, um die Zeile zu aktualisieren
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;Dynamische Werte und where in
Wertelisten können ebenfalls dynamisch erstellt werden, was where-in-Abfragen einfach macht. Optional können Sie ein Array von Objekten übergeben und angeben, welcher Schlüssel zum Erstellen der Liste verwendet werden soll.
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")}`;sql.array-Helfer
Der sql.array-Helfer erstellt PostgreSQL-Array-Literale aus JavaScript-Arrays:
// Array-Literale für PostgreSQL erstellen
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
// Erzeugt: INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])
// Funktioniert auch mit numerischen Arrays
await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
// Erzeugt: SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])NOTE
`sql.array` ist nur für PostgreSQL. Mehrdimensionale Arrays und NULL-Elemente werden möglicherweise noch nicht unterstützt.sql``.simple()
Das PostgreSQL-Wire-Protokoll unterstützt zwei Arten von Abfragen: "simple" und "extended". Einfache Abfragen können mehrere Anweisungen enthalten, unterstützen aber keine Parameter, während erweiterte Abfragen (Standard) Parameter unterstützen, aber nur eine Anweisung zulassen.
Um mehrere Anweisungen in einer einzigen Abfrage auszuführen, verwenden Sie sql``.simple():
// Mehrere Anweisungen in einer Abfrage
await sql`
SELECT 1;
SELECT 2;
`.simple();Einfache Abfragen sind oft nützlich für Datenbank-Migrationen und Setup-Skripte.
Beachten Sie, dass einfache Abfragen keine Parameter (${value}) verwenden können. Wenn Sie Parameter benötigen, müssen Sie Ihre Abfrage in separate Anweisungen aufteilen.
Abfragen in Dateien
Sie können die sql.file-Methode verwenden, um eine Abfrage aus einer Datei zu lesen und auszuführen. Wenn die Datei $1, $2 usw. enthält, können Sie Parameter an die Abfrage übergeben. Wenn keine Parameter verwendet werden, können mehrere Befehle pro Datei ausgeführt werden.
const result = await sql.file("query.sql", [1, 2, 3]);Unsichere Abfragen
Sie können die sql.unsafe-Funktion verwenden, um rohe SQL-Strings auszuführen. Verwenden Sie dies mit Vorsicht, da es Benutzereingaben nicht maskiert. Das Ausführen von mehr als einem Befehl pro Abfrage ist erlaubt, wenn keine Parameter verwendet werden.
// Mehrere Befehle ohne Parameter
const result = await sql.unsafe(`
SELECT ${userColumns} FROM users;
SELECT ${accountColumns} FROM accounts;
`);
// Mit Parametern (nur ein Befehl erlaubt)
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);Ausführen und Abbrechen von Abfragen
Buns SQL ist lazy, was bedeutet, dass es erst mit der Ausführung beginnt, wenn es erwartet oder mit .execute() ausgeführt wird. Sie können eine aktuell ausgeführte Abfrage abbrechen, indem Sie die cancel()-Methode auf dem Abfrageobjekt aufrufen.
const query = sql`SELECT * FROM users`.execute();
setTimeout(() => query.cancel(), 100);
await query;Datenbank-Umgebungsvariablen
sql-Verbindungsparameter können über Umgebungsvariablen konfiguriert werden. Der Client überprüft diese Variablen in einer bestimmten Reihenfolge und erkennt automatisch den Datenbanktyp basierend auf dem Format der Verbindungszeichenfolge.
Automatische Datenbankerkennung
Bei Verwendung von Bun.sql() ohne Argumente oder new SQL() mit einer Verbindungszeichenfolge wird der Adapter automatisch basierend auf dem URL-Format erkannt:
MySQL-Auto-Erkennung
MySQL wird automatisch ausgewählt, wenn die Verbindungszeichenfolge mit diesen Mustern übereinstimmt:
mysql://...- MySQL-Protokoll-URLsmysql2://...- MySQL2-Protokoll-URLs (Kompatibilitäts-Alias)
// Diese verwenden alle automatisch MySQL (kein Adapter erforderlich)
const sql1 = new SQL("mysql://user:pass@localhost/mydb");
const sql2 = new SQL("mysql2://user:pass@localhost:3306/mydb");
// Funktioniert mit DATABASE_URL-Umgebungsvariable
DATABASE_URL="mysql://user:pass@localhost/mydb" bun run app.js
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" bun run app.jsSQLite-Auto-Erkennung
SQLite wird automatisch ausgewählt, wenn die Verbindungszeichenfolge mit diesen Mustern übereinstimmt:
:memory:- In-Memory-Datenbanksqlite://...- SQLite-Protokoll-URLssqlite:...- SQLite-Protokoll ohne Schrägstrichefile://...- File-Protokoll-URLsfile:...- File-Protokoll ohne Schrägstriche
// Diese verwenden alle automatisch SQLite (kein Adapter erforderlich)
const sql1 = new SQL(":memory:");
const sql2 = new SQL("sqlite://app.db");
const sql3 = new SQL("file://./database.db");
// Funktioniert mit DATABASE_URL-Umgebungsvariable
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.jsPostgreSQL-Auto-Erkennung
PostgreSQL ist der Standard für Verbindungszeichenfolgen, die nicht mit MySQL- oder SQLite-Mustern übereinstimmen:
# PostgreSQL wird für diese Muster erkannt
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" bun run app.js
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" bun run app.js
# Oder jede URL, die nicht mit MySQL- oder SQLite-Mustern übereinstimmt
DATABASE_URL="localhost:5432/mydb" bun run app.jsMySQL-Umgebungsvariablen
MySQL-Verbindungen können über Umgebungsvariablen konfiguriert werden:
# Primäre Verbindungs-URL (zuerst geprüft)
MYSQL_URL="mysql://user:pass@localhost:3306/mydb"
# Alternative: DATABASE_URL mit MySQL-Protokoll
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb"Wenn keine Verbindungs-URL angegeben wird, überprüft MySQL diese einzelnen Parameter:
| Umgebungsvariable | Standardwert | Beschreibung |
|---|---|---|
MYSQL_HOST | localhost | Datenbank-Host |
MYSQL_PORT | 3306 | Datenbank-Port |
MYSQL_USER | root | Datenbank-Benutzer |
MYSQL_PASSWORD | (leer) | Datenbank-Passwort |
MYSQL_DATABASE | mysql | Datenbank-Name |
MYSQL_URL | (leer) | Primäre Verbindungs-URL für MySQL |
TLS_MYSQL_DATABASE_URL | (leer) | SSL/TLS-aktivierte Verbindungs-URL |
PostgreSQL-Umgebungsvariablen
Die folgenden Umgebungsvariablen können verwendet werden, um die PostgreSQL-Verbindung zu definieren:
| Umgebungsvariable | Beschreibung |
|---|---|
POSTGRES_URL | Primäre Verbindungs-URL für PostgreSQL |
DATABASE_URL | Alternative Verbindungs-URL (auto-erkannt) |
PGURL | Alternative Verbindungs-URL |
PG_URL | Alternative Verbindungs-URL |
TLS_POSTGRES_DATABASE_URL | SSL/TLS-aktivierte Verbindungs-URL |
TLS_DATABASE_URL | Alternative SSL/TLS-aktivierte Verbindungs-URL |
Wenn keine Verbindungs-URL angegeben wird, überprüft das System die folgenden einzelnen Parameter:
| Umgebungsvariable | Fallback-Variablen | Standardwert | Beschreibung |
|---|---|---|---|
PGHOST | - | localhost | Datenbank-Host |
PGPORT | - | 5432 | Datenbank-Port |
PGUSERNAME | PGUSER, USER, USERNAME | postgres | Datenbank-Benutzer |
PGPASSWORD | - | (leer) | Datenbank-Passwort |
PGDATABASE | - | username | Datenbank-Name |
SQLite-Umgebungsvariablen
SQLite-Verbindungen können über DATABASE_URL konfiguriert werden, wenn es eine SQLite-kompatible URL enthält:
# Diese werden alle als SQLite erkannt
DATABASE_URL=":memory:"
DATABASE_URL="sqlite://./app.db"
DATABASE_URL="file:///absolute/path/to/db.sqlite"Hinweis: PostgreSQL-spezifische Umgebungsvariablen (POSTGRES_URL, PGHOST usw.) werden bei Verwendung von SQLite ignoriert.
Laufzeit-Preconnection
Bun kann sich beim Start mit PostgreSQL vorverbinden, um die Performance zu verbessern, indem Datenbankverbindungen hergestellt werden, bevor Ihr Anwendungscode ausgeführt wird. Dies ist nützlich, um die Verbindungslatenz bei der ersten Datenbankabfrage zu reduzieren.
# PostgreSQL-Preconnection aktivieren
bun --sql-preconnect index.js
# Funktioniert mit DATABASE_URL-Umgebungsvariable
DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.js
# Kann mit anderen Runtime-Flags kombiniert werden
bun --sql-preconnect --hot index.jsDas --sql-preconnect-Flag stellt automatisch eine PostgreSQL-Verbindung unter Verwendung Ihrer konfigurierten Umgebungsvariablen beim Start her. Wenn die Verbindung fehlschlägt, stürzt Ihre Anwendung nicht ab - der Fehler wird gracefully behandelt.
Verbindungsoptionen
Sie können Ihre Datenbankverbindung manuell konfigurieren, indem Sie Optionen an den SQL-Konstruktor übergeben. Die Optionen variieren je nach Datenbank-Adapter:
MySQL-Optionen
import { SQL } from "bun";
const sql = new SQL({
// Erforderlich für MySQL bei Verwendung eines Optionsobjekts
adapter: "mysql",
// Verbindungsdetails
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Unix-Socket-Verbindung (Alternative zu hostname/port)
// socket: "/var/run/mysqld/mysqld.sock",
// Connection-Pool-Einstellungen
max: 20, // Maximale Verbindungen im Pool (Standard: 10)
idleTimeout: 30, // Inaktive Verbindungen nach 30s schließen
maxLifetime: 0, // Verbindungslebensdauer in Sekunden (0 = für immer)
connectionTimeout: 30, // Timeout beim Herstellen neuer Verbindungen
// SSL/TLS-Optionen
ssl: "prefer", // oder "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("Mit MySQL verbunden");
},
onclose: (client, err) => {
if (err) {
console.error("MySQL-Verbindungsfehler:", err);
} else {
console.log("MySQL-Verbindung geschlossen");
}
},
});PostgreSQL-Optionen
import { SQL } from "bun";
const sql = new SQL({
// Verbindungsdetails (Adapter wird automatisch als PostgreSQL erkannt)
url: "postgres://user:pass@localhost:5432/dbname",
// Alternative Verbindungsparameter
hostname: "localhost",
port: 5432,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Connection-Pool-Einstellungen
max: 20, // Maximale Verbindungen im Pool
idleTimeout: 30, // Inaktive Verbindungen nach 30s schließen
maxLifetime: 0, // Verbindungslebensdauer in Sekunden (0 = für immer)
connectionTimeout: 30, // Timeout beim Herstellen neuer Verbindungen
// SSL/TLS-Optionen
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("Mit PostgreSQL verbunden");
},
onclose: client => {
console.log("PostgreSQL-Verbindung geschlossen");
},
});SQLite-Optionen
import { SQL } from "bun";
const sql = new SQL({
// Erforderlich für SQLite
adapter: "sqlite",
filename: "./data/app.db", // oder ":memory:" für In-Memory-Datenbank
// SQLite-spezifische Zugriffsmodi
readonly: false, // Im Nur-Lese-Modus öffnen
create: true, // Datenbank erstellen, falls nicht vorhanden
readwrite: true, // Lese- und Schreiboperationen erlauben
// SQLite-Datenverarbeitung
strict: true, // Strengen Modus für bessere Typsicherheit aktivieren
safeIntegers: false, // BigInt für Ganzzahlen außerhalb des JS-Zahlenbereichs verwenden
// Callbacks
onconnect: client => {
console.log("SQLite-Datenbank geöffnet");
},
onclose: client => {
console.log("SQLite-Datenbank geschlossen");
},
});SQLite-Verbindungshinweise">
- Connection Pooling: SQLite verwendet kein Connection Pooling, da es eine dateibasierte Datenbank ist. Jede
SQL-Instanz stellt eine einzelne Verbindung dar. - Transaktionen: SQLite unterstützt verschachtelte Transaktionen durch Savepoints, ähnlich wie PostgreSQL.
- Gleichzeitiger Zugriff: SQLite handhabt gleichzeitigen Zugriff durch Dateisperrung. Verwenden Sie den WAL-Modus für bessere Parallelität.
- Speicherdatenbanken: Die Verwendung von
:memory:erstellt eine temporäre Datenbank, die nur für die Lebensdauer der Verbindung existiert.
Dynamische Passwörter
Wenn Clients alternative Authentifizierungsschemata wie Zugriffstoken oder Verbindungen zu Datenbanken mit rotierenden Passwörtern verwenden müssen, stellen Sie entweder eine synchrone oder asynchrone Funktion bereit, die den dynamischen Passwortwert zum Zeitpunkt der Verbindung auflöst.
import { SQL } from "bun";
const sql = new SQL(url, {
// Andere Verbindungskonfiguration
...
// Passwort-Funktion für den Datenbankbenutzer
password: async () => await signer.getAuthToken(),
});SQLite-spezifische Funktionen
Abfrageausführung
SQLite führt Abfragen synchron aus, im Gegensatz zu PostgreSQL, das asynchrone I/O verwendet. Die API bleibt jedoch konsistent unter Verwendung von Promises:
const sqlite = new SQL("sqlite://app.db");
// Funktioniert wie PostgreSQL, führt aber intern synchron aus
const users = await sqlite`SELECT * FROM users`;
// Parameter funktionieren identisch
const user = await sqlite`SELECT * FROM users WHERE id = ${userId}`;SQLite-Pragmas
Sie können PRAGMA-Anweisungen verwenden, um das SQLite-Verhalten zu konfigurieren:
const sqlite = new SQL("sqlite://app.db");
// Fremdschlüssel aktivieren
await sqlite`PRAGMA foreign_keys = ON`;
// Journal-Modus auf WAL für bessere Parallelität setzen
await sqlite`PRAGMA journal_mode = WAL`;
// Integrität prüfen
const integrity = await sqlite`PRAGMA integrity_check`;Unterschiede bei Datentypen
SQLite hat ein flexibleres Typsystem als PostgreSQL:
// SQLite speichert Daten in 5 Speicherklassen: NULL, INTEGER, REAL, TEXT, BLOB
const sqlite = new SQL("sqlite://app.db");
// SQLite ist toleranter mit Typen
await sqlite`
CREATE TABLE flexible (
id INTEGER PRIMARY KEY,
data TEXT, -- Kann Zahlen als Strings speichern
value NUMERIC, -- Kann Ganzzahlen, Reals oder Text speichern
blob BLOB -- Binärdaten
)
`;
// JavaScript-Werte werden automatisch konvertiert
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;Transaktionen
Um eine neue Transaktion zu starten, verwenden Sie sql.begin. Diese Methode funktioniert sowohl für PostgreSQL als auch für SQLite. Für PostgreSQL reserviert sie eine dedizierte Verbindung aus dem Pool. Für SQLite beginnt sie eine Transaktion auf der einzelnen Verbindung.
Der BEGIN-Befehl wird automatisch gesendet, einschließlich aller optionalen Konfigurationen, die Sie angeben. Wenn während der Transaktion ein Fehler auftritt, wird ein ROLLBACK ausgelöst, um einen reibungslosen Fortgang des Prozesses sicherzustellen.
Grundlegende Transaktionen
await sql.begin(async tx => {
// Alle Abfragen in dieser Funktion werden in einer Transaktion ausgeführt
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`;
// Transaktion wird automatisch committet, wenn keine Fehler ausgelöst werden
// Rollback, wenn ein Fehler auftritt
});Es ist auch möglich, die Anforderungen in einer Transaktion zu pipelinen, falls erforderlich, indem ein Array mit Abfragen aus der Callback-Funktion zurückgegeben wird:
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
Savepoints in SQL erstellen intermediate Checkpoints innerhalb einer Transaktion und ermöglichen partielle Rollbacks, ohne den gesamten Vorgang zu beeinträchtigen. Sie sind nützlich bei komplexen Transaktionen und ermöglichen Fehlerwiederherstellung und konsistente Ergebnisse.
await sql.begin(async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx.savepoint(async sp => {
// Dieser Teil kann separat zurückgerollt werden
await sp`UPDATE users SET status = 'active'`;
if (someCondition) {
throw new Error("Rollback zum Savepoint");
}
});
// Transaktion fortsetzen, auch wenn Savepoint zurückgerollt wurde
await tx`INSERT INTO audit_log (action) VALUES ('user_created')`;
});Verteilte Transaktionen
Two-Phase Commit (2PC) ist ein verteiltes Transaktionsprotokoll, bei dem Phase 1 den Koordinator hat, der Knoten vorbereitet, indem sichergestellt wird, dass Daten geschrieben und zum Committen bereit sind, während Phase 2 mit Knoten abschließt, die entweder committen oder rollbacken basierend auf der Entscheidung des Koordinators. Dieser Prozess stellt Datenhaltbarkeit und ordnungsgemäßes Lock-Management sicher.
In PostgreSQL und MySQL persistieren verteilte Transaktionen über ihre ursprüngliche Sitzung hinaus und ermöglichen privilegierten Benutzern oder Koordinatoren, sie später zu committen oder zurückzurollen. Dies unterstützt robuste verteilte Transaktionen, Wiederherstellungsprozesse und administrative Vorgänge.
Jedes Datenbanksystem implementiert verteilte Transaktionen unterschiedlich:
PostgreSQL unterstützt sie nativ durch vorbereitete Transaktionen, während MySQL XA-Transaktionen verwendet.
Wenn während der verteilten Transaktion Ausnahmen auftreten und nicht abgefangen werden, rollt das System automatisch alle Änderungen zurück. Wenn alles normal verläuft, behalten Sie die Flexibilität, die Transaktion später entweder zu committen oder zurückzurollen.
// Verteilte Transaktion beginnen
await sql.beginDistributed("tx1", async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
});
// Später committen oder rollbacken
await sql.commitDistributed("tx1");
// oder
await sql.rollbackDistributed("tx1");Authentifizierung
Bun unterstützt SCRAM-SHA-256 (SASL), MD5 und Klartext-Authentifizierung. SASL wird für bessere Sicherheit empfohlen. Weitere Informationen finden Sie unter Postgres SASL Authentication.
SSL-Modi-Übersicht
PostgreSQL unterstützt verschiedene SSL/TLS-Modi, um zu steuern, wie sichere Verbindungen hergestellt werden. Diese Modi bestimmen das Verhalten beim Verbinden und den Grad der Zertifikatsüberprüfung.
const sql = new SQL({
hostname: "localhost",
username: "user",
password: "password",
ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});| SSL-Modus | Beschreibung |
|---|---|
disable | Kein SSL/TLS verwendet. Verbindungen schlagen fehl, wenn Server SSL erfordert. |
prefer | Versucht zuerst SSL, fällt auf nicht-SSL zurück, wenn SSL fehlschlägt. Standardmodus, wenn keiner angegeben. |
require | Erfordert SSL ohne Zertifikatsüberprüfung. Schlägt fehl, wenn SSL nicht hergestellt werden kann. |
verify-ca | Überprüft, ob Server-Zertifikat von vertrauenswürdiger CA signiert ist. Schlägt fehl, wenn Überprüfung fehlschlägt. |
verify-full | Sicherster Modus. Überprüft Zertifikat und Hostnamen-Übereinstimmung. Schützt vor nicht vertrauenswürdigen Zertifikaten und MITM-Angriffen. |
Verwendung mit Verbindungszeichenfolgen
Der SSL-Modus kann auch in Verbindungszeichenfolgen angegeben werden:
// prefer-Modus verwenden
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");
// verify-full-Modus verwenden
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");Connection Pooling
Buns SQL-Client verwaltet automatisch einen Connection Pool, der ein Pool von Datenbankverbindungen ist, die für mehrere Abfragen wiederverwendet werden. Dies hilft, den Overhead beim Herstellen und Schließen von Verbindungen für jede Abfrage zu reduzieren, und hilft auch, die Anzahl der gleichzeitigen Verbindungen zur Datenbank zu verwalten.
const sql = new SQL({
// Pool-Konfiguration
max: 20, // Maximal 20 gleichzeitige Verbindungen
idleTimeout: 30, // Inaktive Verbindungen nach 30s schließen
maxLifetime: 3600, // Maximale Verbindungslebensdauer 1 Stunde
connectionTimeout: 10, // Verbindungs-Timeout 10s
});Es wird keine Verbindung hergestellt, bis eine Abfrage ausgeführt wird.
const sql = Bun.SQL(); // keine Verbindungen werden erstellt
await sql`...`; // Pool wird gestartet, bis max erreicht ist (wenn möglich), erste verfügbare Verbindung wird verwendet
await sql`...`; // vorherige Verbindung wird wiederverwendet
// zwei Verbindungen werden jetzt gleichzeitig verwendet
await Promise.all([
sql`INSERT INTO users ${sql({ name: "Alice" })}`,
sql`UPDATE users SET name = ${user.name} WHERE id = ${user.id}`,
]);
await sql.close(); // Alle Abfragen abschließen lassen und alle Verbindungen aus dem Pool schließen
await sql.close({ timeout: 5 }); // 5 Sekunden warten und alle Verbindungen aus dem Pool schließen
await sql.close({ timeout: 0 }); // Alle Verbindungen aus dem Pool sofort schließenReservierte Verbindungen
Bun ermöglicht es Ihnen, eine Verbindung aus dem Pool zu reservieren, und gibt einen Client zurück, der die einzelne Verbindung umschließt. Dies kann verwendet werden, um Abfragen auf einer isolierten Verbindung auszuführen.
// Exklusive Verbindung aus dem Pool erhalten
const reserved = await sql.reserve();
try {
await reserved`INSERT INTO users (name) VALUES (${"Alice"})`;
} finally {
// Wichtig: Verbindung zurück an den Pool freigeben
reserved.release();
}
// Oder mit Symbol.dispose verwenden
{
using reserved = await sql.reserve();
await reserved`SELECT 1`;
} // Automatisch freigegebenVorbereitete Anweisungen
Standardmäßig erstellt Buns SQL-Client automatisch benannte vorbereitete Anweisungen für Abfragen, bei denen abgeleitet werden kann, dass die Abfrage statisch ist. Dies bietet bessere Performance. Sie können dieses Verhalten jedoch ändern, indem Sie prepare: false in den Verbindungsoptionen setzen:
const sql = new SQL({
// ... andere Optionen ...
prepare: false, // Persistieren benannter vorbereiteter Anweisungen auf dem Server deaktivieren
});Wenn prepare: false gesetzt ist:
Abfragen werden weiterhin mit dem "extended"-Protokoll ausgeführt, aber sie werden mit unbenannten vorbereiteten Anweisungen ausgeführt. Eine unbenannte vorbereitete Anweisung dauert nur bis zur nächsten Parse-Anweisung, die die unbenannte Anweisung als Ziel angibt.
- Parameter-Bindung ist weiterhin sicher gegen SQL-Injection
- Jede Abfrage wird vom Server von Grund auf geparst und geplant
- Abfragen werden nicht gepipelined
Sie möchten prepare: false möglicherweise verwenden, wenn:
- Verwendung von PGBouncer im Transaktionsmodus (obwohl seit PGBouncer 1.21.0 Protokoll-Level benannte vorbereitete Anweisungen unterstützt werden, wenn ordnungsgemäß konfiguriert)
- Debugging von Abfrageausführungsplänen
- Arbeiten mit dynamischem SQL, bei dem Abfragepläne häufig neu generiert werden müssen
- Mehr als ein Befehl pro Abfrage wird nicht unterstützt (es sei denn, Sie verwenden
sql``.simple())
Beachten Sie, dass das Deaktivieren vorbereiteter Anweisungen die Performance für Abfragen beeinträchtigen kann, die häufig mit verschiedenen Parametern ausgeführt werden, da der Server jede Abfrage von Grund auf parsen und planen muss.
Fehlerbehandlung
Der Client bietet typisierte Fehler für verschiedene Fehlerszenarien. Fehler sind datenbankspezifisch und erweitern Basisklassen für Fehler:
Fehlerklassen
import { SQL } from "bun";
try {
await sql`SELECT * FROM users`;
} catch (error) {
if (error instanceof SQL.PostgresError) {
// PostgreSQL-spezifischer Fehler
console.log(error.code); // PostgreSQL-Fehlercode
console.log(error.detail); // Detaillierte Fehlermeldung
console.log(error.hint); // Hilfreicher Hinweis von PostgreSQL
} else if (error instanceof SQL.SQLiteError) {
// SQLite-spezifischer Fehler
console.log(error.code); // SQLite-Fehlercode (z.B. "SQLITE_CONSTRAINT")
console.log(error.errno); // SQLite-Fehlernummer
console.log(error.byteOffset); // Byte-Offset in SQL-Anweisung (falls verfügbar)
} else if (error instanceof SQL.SQLError) {
// Generischer SQL-Fehler (Basisklasse)
console.log(error.message);
}
}PostgreSQL-spezifische Fehlercodes">
PostgreSQL-Verbindungsfehler
| Verbindungsfehler | Beschreibung |
|---|---|
ERR_POSTGRES_CONNECTION_CLOSED | Verbindung wurde getrennt oder nie hergestellt |
ERR_POSTGRES_CONNECTION_TIMEOUT | Verbindungsherstellung innerhalb des Timeout-Zeitraums fehlgeschlagen |
ERR_POSTGRES_IDLE_TIMEOUT | Verbindung wegen Inaktivität geschlossen |
ERR_POSTGRES_LIFETIME_TIMEOUT | Verbindung hat maximale Lebensdauer überschritten |
ERR_POSTGRES_TLS_NOT_AVAILABLE | SSL/TLS-Verbindung nicht verfügbar |
ERR_POSTGRES_TLS_UPGRADE_FAILED | Upgrade der Verbindung auf SSL/TLS fehlgeschlagen |
Authentifizierungsfehler
| Authentifizierungsfehler | Beschreibung |
|---|---|
ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2 | Passwort-Authentifizierung fehlgeschlagen |
ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD | Server hat unbekannte Auth-Methode angefordert |
ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD | Server hat nicht unterstützte Auth-Methode angefordert |
ERR_POSTGRES_INVALID_SERVER_KEY | Ungültiger Serverschlüssel während der Authentifizierung |
ERR_POSTGRES_INVALID_SERVER_SIGNATURE | Ungültige Serversignatur |
ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64 | Ungültige SASL-Signatur-Codierung |
ERR_POSTGRES_SASL_SIGNATURE_MISMATCH | SASL-Signatur-Überprüfung fehlgeschlagen |
Abfragefehler
| Abfragefehler | Beschreibung |
|---|---|
ERR_POSTGRES_SYNTAX_ERROR | Ungültige SQL-Syntax (erweitert SyntaxError) |
ERR_POSTGRES_SERVER_ERROR | Allgemeiner Fehler vom PostgreSQL-Server |
ERR_POSTGRES_INVALID_QUERY_BINDING | Ungültige Parameter-Bindung |
ERR_POSTGRES_QUERY_CANCELLED | Abfrage wurde abgebrochen |
ERR_POSTGRES_NOT_TAGGED_CALL | Abfrage wurde ohne getaggten Aufruf aufgerufen |
Datentyp-Fehler
| Datentyp-Fehler | Beschreibung |
|---|---|
ERR_POSTGRES_INVALID_BINARY_DATA | Ungültiges Binärdaten-Format |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE | Ungültige Byte-Sequenz |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING | Codierungsfehler |
ERR_POSTGRES_INVALID_CHARACTER | Ungültiges Zeichen in Daten |
ERR_POSTGRES_OVERFLOW | Numerischer Überlauf |
ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT | Nicht unterstütztes Binärformat |
ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE | Ganzzahlgröße nicht unterstützt |
ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET | Mehrdimensionale Arrays nicht unterstützt |
ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET | NULL-Werte in Arrays nicht unterstützt |
Protokollfehler
| Protokollfehler | Beschreibung |
|---|---|
ERR_POSTGRES_EXPECTED_REQUEST | Client-Anfrage erwartet |
ERR_POSTGRES_EXPECTED_STATEMENT | Vorbereitete Anweisung erwartet |
ERR_POSTGRES_INVALID_BACKEND_KEY_DATA | Ungültige Backend-Schlüsseldaten |
ERR_POSTGRES_INVALID_MESSAGE | Ungültige Protokollnachricht |
ERR_POSTGRES_INVALID_MESSAGE_LENGTH | Ungültige Nachrichtenlänge |
ERR_POSTGRES_UNEXPECTED_MESSAGE | Unerwarteter Nachrichtentyp |
Transaktionsfehler
| Transaktionsfehler | Beschreibung |
|---|---|
ERR_POSTGRES_UNSAFE_TRANSACTION | Unsichere Transaktionsoperation erkannt |
ERR_POSTGRES_INVALID_TRANSACTION_STATE | Ungültiger Transaktionszustand |
SQLite-spezifische Fehler
SQLite-Fehler bieten Fehlercodes und Nummern, die den Standard-Fehlercodes von SQLite entsprechen:
Häufige SQLite-Fehlercodes">
| Fehlercode | errno | Beschreibung |
|---|---|---|
SQLITE_CONSTRAINT | 19 | Constraint-Verletzung (UNIQUE, CHECK, NOT NULL usw.) |
SQLITE_BUSY | 5 | Datenbank ist gesperrt |
SQLITE_LOCKED | 6 | Tabelle in der Datenbank ist gesperrt |
SQLITE_READONLY | 8 | Versuch, in eine schreibgeschützte Datenbank zu schreiben |
SQLITE_IOERR | 10 | Festplatten-I/O-Fehler |
SQLITE_CORRUPT | 11 | Datenbank-Disk-Image ist beschädigt |
SQLITE_FULL | 13 | Datenbank oder Festplatte ist voll |
SQLITE_CANTOPEN | 14 | Datenbankdatei kann nicht geöffnet werden |
SQLITE_PROTOCOL | 15 | Datenbank-Sperrprotokollfehler |
SQLITE_SCHEMA | 17 | Datenbank-Schema hat sich geändert |
SQLITE_TOOBIG | 18 | String oder BLOB überschreitet Größenlimit |
SQLITE_MISMATCH | 20 | Datentyp-Unstimmigkeit |
SQLITE_MISUSE | 21 | Bibliothek falsch verwendet |
SQLITE_AUTH | 23 | Autorisierung verweigert |
Beispiel-Fehlerbehandlung:
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')`; // Doppelte ID
} catch (error) {
if (error instanceof SQL.SQLiteError) {
if (error.code === "SQLITE_CONSTRAINT") {
console.log("Constraint-Verletzung:", error.message);
// Einzigartige Constraint-Verletzung behandeln
}
}
}Zahlen und BigInt
Buns SQL-Client beinhaltet spezielle Behandlung für große Zahlen, die den Bereich einer 53-Bit-Ganzzahl überschreiten. So funktioniert es:
import { sql } from "bun";
const [{ x, y }] = await sql`SELECT 9223372036854777 as x, 12345 as y`;
console.log(typeof x, x); // "string" "9223372036854777"
console.log(typeof y, y); // "number" 12345BigInt anstelle von Strings
Wenn Sie große Zahlen als BigInt anstelle von Strings benötigen, können Sie dies aktivieren, indem Sie die bigint-Option auf true setzen, wenn Sie den SQL-Client initialisieren:
const sql = new SQL({
bigint: true,
});
const [{ x }] = await sql`SELECT 9223372036854777 as x`;
console.log(typeof x, x); // "bigint" 9223372036854777nRoadmap
Es gibt noch einige Dinge, die wir noch nicht fertiggestellt haben.
- Verbindungsvorladung über
--db-preconnectBun CLI-Flag - Spaltenname-Transformationen (z.B.
snake_casezucamelCase). Dies ist größtenteils blockiert durch eine Unicode-bewusste Implementierung der Groß-/Kleinschreibungsänderung in C++ unter Verwendung von WebKitsWTF::String. - Spaltentyp-Transformationen
Datenbankspezifische Funktionen
Authentifizierungsmethoden
MySQL unterstützt mehrere Authentifizierungs-Plugins, die automatisch ausgehandelt werden:
mysql_native_password- Traditionelle MySQL-Authentifizierung, weitgehend kompatibelcaching_sha2_password- Standard in MySQL 8.0+, sicherer mit RSA-Schlüsselaustauschsha256_password- SHA-256-basierte Authentifizierung
Der Client handhabt automatisch das Wechseln des Authentifizierungs-Plugins, wenn vom Server angefordert, einschließlich sicherem Passwortaustausch über nicht-SSL-Verbindungen.
Vorbereitete Anweisungen und Performance
MySQL verwendet serverseitige vorbereitete Anweisungen für alle parametrisierten Abfragen:
// Dies erstellt automatisch eine vorbereitete Anweisung auf dem Server
const user = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Vorbereitete Anweisungen werden gecacht und für identische Abfragen wiederverwendet
for (const id of userIds) {
// Gleiche vorbereitete Anweisung wird wiederverwendet
await mysql`SELECT * FROM users WHERE id = ${id}`;
}
// Abfrage-Pipelining - mehrere Anweisungen werden ohne Warten auf Antworten gesendet
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}`,
]);Mehrere Ergebnissätze
MySQL kann mehrere Ergebnissätze aus Multi-Statement-Abfragen zurückgeben:
const mysql = new SQL("mysql://user:pass@localhost/mydb");
// Multi-Statement-Abfragen mit simple()-Methode
const multiResults = await mysql`
SELECT * FROM users WHERE id = 1;
SELECT * FROM orders WHERE user_id = 1;
`.simple();Zeichensätze und Kollationen
Bun.SQL verwendet automatisch den utf8mb4-Zeichensatz für MySQL-Verbindungen und stellt vollständige Unicode-Unterstützung einschließlich Emojis sicher. Dies ist der empfohlene Zeichensatz für moderne MySQL-Anwendungen.
Verbindungsattribute
Bun sendet automatisch Client-Informationen an MySQL für bessere Überwachung:
// Diese Attribute werden automatisch gesendet:
// _client_name: "Bun"
// _client_version: <bun version>
// Sie können diese in MySQLs performance_schema.session_connect_attrs sehenTypbehandlung
MySQL-Typen werden automatisch in JavaScript-Typen konvertiert:
| MySQL-Typ | JavaScript-Typ | Hinweise |
|---|---|---|
| INT, TINYINT, MEDIUMINT | number | Innerhalb des sicheren Ganzzahlbereichs |
| BIGINT | string, number oder BigInt | Wenn der Wert in i32/u32 passt, ist es number, sonst string oder BigInt basierend auf bigint-Option |
| DECIMAL, NUMERIC | string | Zur Erhaltung der Präzision |
| FLOAT, DOUBLE | number | |
| DATE | Date | JavaScript-Date-Objekt |
| DATETIME, TIMESTAMP | Date | Mit Zeitzonenbehandlung |
| TIME | number | Summe der Mikrosekunden |
| YEAR | number | |
| CHAR, VARCHAR, VARSTRING, STRING | string | |
| TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXT | string | |
| TINY BLOB, MEDIUM BLOB, BLOG, LONG BLOB | string | BLOB-Typen sind Alias für TEXT-Typen |
| JSON | object/array | Automatisch geparst |
| BIT(1) | boolean | BIT(1) in MySQL |
| GEOMETRY | string | Geometriedaten |
Unterschiede zu PostgreSQL
Obwohl die API vereinheitlicht ist, gibt es einige Verhaltensunterschiede:
- Parameter-Platzhalter: MySQL verwendet intern
?, aber Bun konvertiert$1, $2-Stil automatisch - RETURNING-Klausel: MySQL unterstützt RETURNING nicht; verwenden Sie
result.lastInsertRowidoder eine separate SELECT - Array-Typen: MySQL hat keine nativen Array-Typen wie PostgreSQL
MySQL-spezifische Funktionen
Wir haben LOAD DATA INFILE-Unterstützung noch nicht implementiert
PostgreSQL-spezifische Funktionen
Wir haben diese noch nicht implementiert:
COPY-UnterstützungLISTEN-UnterstützungNOTIFY-Unterstützung
Wir haben auch einige der ungewöhnlicheren Funktionen noch nicht implementiert:
- GSSAPI-Authentifizierung
SCRAM-SHA-256-PLUS-Unterstützung- Point- und PostGIS-Typen
- Alle mehrdimensionalen Ganzzahl-Array-Typen (nur einige der Typen werden unterstützt)
Häufige Muster und bewährte Praktiken
Arbeiten mit MySQL-Ergebnissätzen
// Insert-ID nach INSERT erhalten
const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
console.log(result.lastInsertRowid); // MySQLs LAST_INSERT_ID()
// Betroffene Zeilen handhaben
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
console.log(updated.affectedRows); // Anzahl der aktualisierten Zeilen
// MySQL-spezifische Funktionen verwenden
const now = await mysql`SELECT NOW() as current_time`;
const uuid = await mysql`SELECT UUID() as id`;MySQL-Fehlerbehandlung
try {
await mysql`INSERT INTO users (email) VALUES (${"duplicate@email.com"})`;
} catch (error) {
if (error.code === "ER_DUP_ENTRY") {
console.log("Doppelter Eintrag erkannt");
} else if (error.code === "ER_ACCESS_DENIED_ERROR") {
console.log("Zugriff verweigert");
} else if (error.code === "ER_BAD_DB_ERROR") {
console.log("Datenbank existiert nicht");
}
// MySQL-Fehlercodes sind kompatibel mit mysql/mysql2-Paketen
}Performance-Tipps für MySQL
- Connection Pooling verwenden: Angemessene
max-Pool-Größe basierend auf Ihrer Arbeitslast setzen - Vorbereitete Anweisungen aktivieren: Sie sind standardmäßig aktiviert und verbessern die Performance
- Transaktionen für Massenoperationen verwenden: Verwandte Abfragen in Transaktionen gruppieren
- Ordentlich indexieren: MySQL verlässt sich stark auf Indizes für Abfrage-Performance
utf8mb4-Zeichensatz verwenden: Er ist standardmäßig gesetzt und handhabt alle Unicode-Zeichen
Häufig gestellte Fragen
Warum ist das `Bun.sql` und nicht `Bun.postgres`?
Der Plan war, in Zukunft weitere Datenbank-Treiber hinzuzufügen. Jetzt mit MySQL-Unterstützung unterstützt diese vereinheitlichte API PostgreSQL, MySQL und SQLite.
Woher weiß ich, welcher Datenbank-Adapter verwendet wird?
Der Adapter wird automatisch aus der Verbindungszeichenfolge erkannt:
- URLs, die mit `mysql://` oder `mysql2://` beginnen, verwenden MySQL
- URLs, die mit SQLite-Mustern übereinstimmen (`:memory:`, `sqlite://`, `file://`), verwenden SQLite
- Alles andere verwendet standardmäßig PostgreSQL
Werden MySQL-gespeicherte Prozeduren unterstützt?">
Ja, gespeicherte Prozeduren werden vollständig unterstützt, einschließlich OUT-Parametern und mehreren Ergebnissätzen:
```ts
// Gespeicherte Prozedur aufrufen
const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;
// OUT-Parameter erhalten
const outParam = await mysql`SELECT @total_orders as total`;
```
Kann ich MySQL-spezifische SQL-Syntax verwenden?">
Ja, Sie können jede MySQL-spezifische Syntax verwenden:
```ts
// MySQL-spezifische Syntax funktioniert einwandfrei
await mysql`SET @user_id = ${userId}`;
await mysql`SHOW TABLES`;
await mysql`DESCRIBE users`;
await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
```
Warum nicht einfach eine bestehende Bibliothek verwenden?
npm-Pakete wie postgres.js, pg und node-postgres können auch in Bun verwendet werden. Sie sind großartige Optionen.
Zwei Gründe, warum:
- Wir denken, es ist einfacher für Entwickler, einen Datenbank-Treiber in Bun integriert zu haben. Die Zeit, die Sie mit Bibliothekssuche verbringen, ist Zeit, die Sie für den Aufbau Ihrer App verwenden könnten.
- Wir nutzen einige JavaScriptCore-Engine-Interna, um das Erstellen von Objekten schneller zu machen, was in einer Bibliothek schwierig zu implementieren wäre
Danksagungen
Großer Dank an @porsagers postgres.js für die Inspiration für die API-Schnittstelle.