L'interface est conçue pour être simple et performante, utilisant des template literals tagués pour les requêtes et offrant des fonctionnalités telles que la mise en pool des connexions, les transactions et les instructions préparées.
import { sql, SQL } from "bun";
// PostgreSQL (par défaut)
const users = await sql`
SELECT * FROM users
WHERE active = ${true}
LIMIT ${10}
`;
// Avec MySQL
const mysql = new SQL("mysql://user:pass@localhost:3306/mydb");
const mysqlResults = await mysql`
SELECT * FROM users
WHERE active = ${true}
`;
// Avec SQLite
const sqlite = new SQL("sqlite://myapp.db");
const sqliteResults = await sqlite`
SELECT * FROM users
WHERE active = ${1}
`;Fonctionnalités
- Template literals tagués pour se protéger contre les injections SQL
- Transactions
- Paramètres nommés et positionnels
- Mise en pool des connexions
- Prise en charge de
BigInt - Prise en charge de l'authentification SASL (SCRAM-SHA-256), MD5 et texte clair
- Délais d'expiration des connexions
- Retour des lignes sous forme d'objets de données, de tableaux de tableaux ou de Buffer
- Prise en charge du protocole binaire pour plus de rapidité
- Prise en charge de TLS (et mode d'authentification)
- Configuration automatique avec variable d'environnement
Prise en charge des bases de données
Bun.SQL fournit une API unifiée pour plusieurs systèmes de bases de données :
PostgreSQL
PostgreSQL est utilisé lorsque :
- La chaîne de connexion ne correspond pas aux modèles SQLite ou MySQL (c'est l'adaptateur de repli)
- La chaîne de connexion utilise explicitement les protocoles
postgres://oupostgresql:// - Aucune chaîne de connexion n'est fournie et les variables d'environnement pointent vers PostgreSQL
import { sql } from "bun";
// Utilise PostgreSQL si DATABASE_URL n'est pas défini ou est une URL PostgreSQL
await sql`SELECT ...`;
import { SQL } from "bun";
const pg = new SQL("postgres://user:pass@localhost:5432/mydb");
await pg`SELECT ...`;MySQL
La prise en charge de MySQL est intégrée dans Bun.SQL, fournissant la même interface de template literal tagué avec une compatibilité complète pour MySQL 5.7+ et MySQL 8.0+ :
import { SQL } from "bun";
// Connexion MySQL
const mysql = new SQL("mysql://user:password@localhost:3306/database");
const mysql2 = new SQL("mysql2://user:password@localhost:3306/database"); // le protocole mysql2 fonctionne aussi
// Utilisation d'un objet d'options
const mysql3 = new SQL({
adapter: "mysql",
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
});
// Fonctionne avec des paramètres - utilise automatiquement des instructions préparées
const users = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Les transactions fonctionnent comme avec 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}`;
});
// Insertions en bloc
const newUsers = [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
];
await mysql`INSERT INTO users ${mysql(newUsers)}`;Formats de chaîne de connexion MySQL">
MySQL accepte différents formats d'URL pour les chaînes de connexion :
// Protocole standard mysql://
new SQL("mysql://user:pass@localhost:3306/database");
new SQL("mysql://user:pass@localhost/database"); // Port par défaut 3306
// Protocole mysql2:// (compatibilité avec le package npm mysql2)
new SQL("mysql2://user:pass@localhost:3306/database");
// Avec des paramètres de requête
new SQL("mysql://user:pass@localhost/db?ssl=true");
// Connexion via socket Unix
new SQL("mysql://user:pass@/database?socket=/var/run/mysqld/mysqld.sock");Fonctionnalités spécifiques à MySQL">
Les bases de données MySQL prennent en charge :
- Instructions préparées : Créées automatiquement pour les requêtes paramétrées avec mise en cache des instructions
- Protocole binaire : Pour de meilleures performances avec les instructions préparées et une gestion précise des types
- Jeux de résultats multiples : Prise en charge des procédures stockées renvoyant plusieurs jeux de résultats
- Plugins d'authentification : Prise en charge de mysql_native_password, caching_sha2_password (par défaut MySQL 8.0) et sha256_password
- Connexions SSL/TLS : Modes SSL configurables similaires à PostgreSQL
- Attributs de connexion : Informations client envoyées au serveur pour la surveillance
- Pipelining de requêtes : Exécuter plusieurs instructions préparées sans attendre les réponses
SQLite
La prise en charge de SQLite est intégrée dans Bun.SQL, fournissant la même interface de template literal tagué :
import { SQL } from "bun";
// Base de données en mémoire
const memory = new SQL(":memory:");
const memory2 = new SQL("sqlite://:memory:");
// Base de données basée sur un fichier
const sql1 = new SQL("sqlite://myapp.db");
// Utilisation d'un objet d'options
const sql2 = new SQL({
adapter: "sqlite",
filename: "./data/app.db",
});
// Pour les noms de fichiers simples, spécifiez l'adaptateur explicitement
const sql3 = new SQL("myapp.db", { adapter: "sqlite" });Formats de chaîne de connexion SQLite">
SQLite accepte différents formats d'URL pour les chaînes de connexion :
// Protocole standard sqlite://
new SQL("sqlite://path/to/database.db");
new SQL("sqlite:path/to/database.db"); // Sans barres obliques
// Protocole file:// (également reconnu comme SQLite)
new SQL("file://path/to/database.db");
new SQL("file:path/to/database.db");
// Base de données spéciale :memory:
new SQL(":memory:");
new SQL("sqlite://:memory:");
new SQL("file://:memory:");
// Chemins relatifs et absolus
new SQL("sqlite://./local.db"); // Relatif au répertoire actuel
new SQL("sqlite://../parent/db.db"); // Répertoire parent
new SQL("sqlite:///absolute/path.db"); // Chemin absolu
// Avec des paramètres de requête
new SQL("sqlite://data.db?mode=ro"); // Mode lecture seule
new SQL("sqlite://data.db?mode=rw"); // Mode lecture-écriture (pas de création)
new SQL("sqlite://data.db?mode=rwc"); // Mode lecture-écriture-création (par défaut)<Note>
Les noms de fichiers simples sans protocole (comme `"myapp.db"`) nécessitent de spécifier explicitement `{ adapter: "sqlite" }` pour éviter toute ambiguïté avec PostgreSQL.
</Note>
Options spécifiques à SQLite">
Les bases de données SQLite prennent en charge des options de configuration supplémentaires :
const sql = new SQL({
adapter: "sqlite",
filename: "app.db",
// Options spécifiques à SQLite
readonly: false, // Ouvrir en mode lecture seule
create: true, // Créer la base de données si elle n'existe pas
readwrite: true, // Ouvrir pour la lecture et l'écriture
// Options Bun:sqlite supplémentaires
strict: true, // Activer le mode strict
safeIntegers: false, // Utiliser les nombres JavaScript pour les entiers
});Les paramètres de requête dans l'URL sont analysés pour définir ces options :
?mode=ro→readonly: true?mode=rw→readonly: false, create: false?mode=rwc→readonly: false, create: true(par défaut)
Insertion de données
Vous pouvez passer des valeurs JavaScript directement dans le template literal SQL et l'échappement sera géré pour vous.
import { sql } from "bun";
// Insertion de base avec des valeurs directes
const [user] = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING *
`;
// Utilisation de l'aide d'objet pour une syntaxe plus propre
const userData = {
name: "Alice",
email: "alice@example.com",
};
const [newUser] = await sql`
INSERT INTO users ${sql(userData)}
RETURNING *
`;
// Se développe en : INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')Insertion en bloc
Vous pouvez également passer des tableaux d'objets au template literal SQL et il sera développé en une instruction INSERT INTO ... VALUES ....
const users = [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
{ name: "Charlie", email: "charlie@example.com" },
];
await sql`INSERT INTO users ${sql(users)}`;Sélection des colonnes à insérer
Vous pouvez utiliser sql(object, ...string) pour choisir quelles colonnes insérer. Chacune des colonnes doit être définie sur l'objet.
const user = {
name: "Alice",
email: "alice@example.com",
age: 25,
};
await sql`INSERT INTO users ${sql(user, "name", "email")}`;
// Insère uniquement les colonnes name et email, en ignorant les autres champsRésultats de requête
Par défaut, le client SQL de Bun renvoie les résultats de requête sous forme de tableaux d'objets, où chaque objet représente une ligne avec les noms de colonnes comme clés. Cependant, il y a des cas où vous pourriez vouloir les données dans un format différent. Le client fournit deux méthodes supplémentaires à cet effet.
Format sql``.values()
La méthode sql``.values() renvoie les lignes sous forme de tableaux de valeurs plutôt que d'objets. Chaque ligne devient un tableau où les valeurs sont dans le même ordre que les colonnes de votre requête.
const rows = await sql`SELECT * FROM users`.values();
console.log(rows);Cela renvoie quelque chose comme :
[
["Alice", "alice@example.com"],
["Bob", "bob@example.com"],
];sql``.values() est particulièrement utile si des noms de colonnes en double sont renvoyés dans les résultats de la requête. Lors de l'utilisation d'objets (par défaut), le dernier nom de colonne est utilisé comme clé dans l'objet, ce qui signifie que les noms de colonnes en double s'écrasent les uns les autres — mais lors de l'utilisation de sql``.values(), chaque colonne est présente dans le tableau afin que vous puissiez accéder aux valeurs des colonnes en double par index.
Format sql``.raw()
La méthode .raw() renvoie les lignes sous forme de tableaux d'objets Buffer. Cela peut être utile pour travailler avec des données binaires ou pour des raisons de performance.
const rows = await sql`SELECT * FROM users`.raw();
console.log(rows); // [[Buffer, Buffer], [Buffer, Buffer], [Buffer, Buffer]]Fragments SQL
Un besoin courant dans les applications de base de données est la capacité de construire des requêtes dynamiquement en fonction des conditions d'exécution. Bun fournit des moyens sûrs de le faire sans risquer d'injection SQL.
Noms de tables dynamiques
Lorsque vous devez référencer des tables ou des schémas dynamiquement, utilisez l'aide sql() pour assurer un échappement approprié :
// Référencer les tables en toute sécurité de manière dynamique
await sql`SELECT * FROM ${sql("users")}`;
// Avec qualification de schéma
await sql`SELECT * FROM ${sql("public.users")}`;Requêtes conditionnelles
Vous pouvez utiliser l'aide sql() pour construire des requêtes avec des clauses conditionnelles. Cela vous permet de créer des requêtes flexibles qui s'adaptent aux besoins de votre application :
// Clauses WHERE optionnelles
const filterAge = true;
const minAge = 21;
const ageFilter = sql`AND age > ${minAge}`;
await sql`
SELECT * FROM users
WHERE active = ${true}
${filterAge ? ageFilter : sql``}
`;Colonnes dynamiques dans les mises à jour
Vous pouvez utiliser sql(object, ...string) pour choisir quelles colonnes mettre à jour. Chacune des colonnes doit être définie sur l'objet. Si les colonnes ne sont pas renseignées, toutes les clés seront utilisées pour mettre à jour la ligne.
await sql`UPDATE users SET ${sql(user, "name", "email")} WHERE id = ${user.id}`;
// utilise toutes les clés de l'objet pour mettre à jour la ligne
await sql`UPDATE users SET ${sql(user)} WHERE id = ${user.id}`;Valeurs dynamiques et where in
Les listes de valeurs peuvent également être créées dynamiquement, rendant les requêtes where in simples aussi. Vous pouvez éventuellement passer un tableau d'objets et indiquer quelle clé utiliser pour créer la liste.
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")}`;Aide sql.array
L'aide sql.array crée des littéraux de tableau PostgreSQL à partir de tableaux JavaScript :
// Créer des littéraux de tableau pour PostgreSQL
await sql`INSERT INTO tags (items) VALUES (${sql.array(["red", "blue", "green"])})`;
// Génère : INSERT INTO tags (items) VALUES (ARRAY['red', 'blue', 'green'])
// Fonctionne avec les tableaux numériques aussi
await sql`SELECT * FROM products WHERE ids = ANY(${sql.array([1, 2, 3])})`;
// Génère : SELECT * FROM products WHERE ids = ANY(ARRAY[1, 2, 3])NOTE
`sql.array` est spécifique à PostgreSQL. Les tableaux multidimensionnels et les éléments NULL peuvent ne pas être encore pris en charge.sql``.simple()
Le protocole filaire PostgreSQL prend en charge deux types de requêtes : "simple" et "extended". Les requêtes simples peuvent contenir plusieurs instructions mais ne prennent pas en charge les paramètres, tandis que les requêtes étendues (par défaut) prennent en charge les paramètres mais n'autorisent qu'une seule instruction.
Pour exécuter plusieurs instructions dans une seule requête, utilisez sql``.simple() :
// Plusieurs instructions dans une requête
await sql`
SELECT 1;
SELECT 2;
`.simple();Les requêtes simples sont souvent utiles pour les migrations de bases de données et les scripts de configuration.
Notez que les requêtes simples ne peuvent pas utiliser de paramètres (${value}). Si vous avez besoin de paramètres, vous devez diviser votre requête en instructions séparées.
Requêtes dans des fichiers
Vous pouvez utiliser la méthode sql.file pour lire une requête depuis un fichier et l'exécuter, si le fichier inclut $1, $2, etc. vous pouvez passer des paramètres à la requête. Si aucun paramètre n'est utilisé, elle peut exécuter plusieurs commandes par fichier.
const result = await sql.file("query.sql", [1, 2, 3]);Requêtes non sécurisées
Vous pouvez utiliser la fonction sql.unsafe pour exécuter des chaînes SQL brutes. Utilisez-la avec prudence, car elle n'échappera pas les entrées utilisateur. L'exécution de plusieurs commandes par requête est autorisée si aucun paramètre n'est utilisé.
// Plusieurs commandes sans paramètres
const result = await sql.unsafe(`
SELECT ${userColumns} FROM users;
SELECT ${accountColumns} FROM accounts;
`);
// Utilisation de paramètres (une seule commande est autorisée)
const result = await sql.unsafe("SELECT " + dangerous + " FROM users WHERE id = $1", [id]);Exécution et annulation de requêtes
Le SQL de Bun est paresseux, ce qui signifie qu'il ne commencera à s'exécuter que lorsqu'il sera attendu ou exécuté avec .execute(). Vous pouvez annuler une requête en cours d'exécution en appelant la méthode cancel() sur l'objet requête.
const query = sql`SELECT * FROM users`.execute();
setTimeout(() => query.cancel(), 100);
await query;Variables d'environnement de la base de données
Les paramètres de connexion sql peuvent être configurés à l'aide de variables d'environnement. Le client vérifie ces variables dans un ordre de priorité spécifique et détecte automatiquement le type de base de données en fonction du format de la chaîne de connexion.
Détection automatique de la base de données
Lors de l'utilisation de Bun.sql() sans arguments ou new SQL() avec une chaîne de connexion, l'adaptateur est automatiquement détecté en fonction du format de l'URL :
Détection automatique de MySQL
MySQL est automatiquement sélectionné lorsque la chaîne de connexion correspond à ces modèles :
mysql://...- URLs de protocole MySQLmysql2://...- URLs de protocole MySQL2 (alias de compatibilité)
// Tous utilisent MySQL automatiquement (pas besoin d'adaptateur)
const sql1 = new SQL("mysql://user:pass@localhost/mydb");
const sql2 = new SQL("mysql2://user:pass@localhost:3306/mydb");
// Fonctionne avec la variable d'environnement DATABASE_URL
DATABASE_URL="mysql://user:pass@localhost/mydb" bun run app.js
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb" bun run app.jsDétection automatique de SQLite
SQLite est automatiquement sélectionné lorsque la chaîne de connexion correspond à ces modèles :
:memory:- Base de données en mémoiresqlite://...- URLs de protocole SQLitesqlite:...- Protocole SQLite sans barres obliquesfile://...- URLs de protocole de fichierfile:...- Protocole de fichier sans barres obliques
// Tous utilisent SQLite automatiquement (pas besoin d'adaptateur)
const sql1 = new SQL(":memory:");
const sql2 = new SQL("sqlite://app.db");
const sql3 = new SQL("file://./database.db");
// Fonctionne avec la variable d'environnement 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.jsDétection automatique de PostgreSQL
PostgreSQL est la valeur par défaut pour les chaînes de connexion qui ne correspondent pas aux modèles MySQL ou SQLite :
# PostgreSQL est détecté pour ces modèles
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" bun run app.js
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb" bun run app.js
# Ou toute URL qui ne correspond pas aux modèles MySQL ou SQLite
DATABASE_URL="localhost:5432/mydb" bun run app.jsVariables d'environnement MySQL
Les connexions MySQL peuvent être configurées via des variables d'environnement :
# URL de connexion principale (vérifiée en premier)
MYSQL_URL="mysql://user:pass@localhost:3306/mydb"
# Alternative : DATABASE_URL avec protocole MySQL
DATABASE_URL="mysql://user:pass@localhost:3306/mydb"
DATABASE_URL="mysql2://user:pass@localhost:3306/mydb"Si aucune URL de connexion n'est fournie, MySQL vérifie ces paramètres individuels :
| Variable d'environnement | Valeur par défaut | Description |
|---|---|---|
MYSQL_HOST | localhost | Hôte de la base de données |
MYSQL_PORT | 3306 | Port de la base de données |
MYSQL_USER | root | Utilisateur de la base de données |
MYSQL_PASSWORD | (vide) | Mot de passe de la base de données |
MYSQL_DATABASE | mysql | Nom de la base de données |
MYSQL_URL | (vide) | URL de connexion principale pour MySQL |
TLS_MYSQL_DATABASE_URL | (vide) | URL de connexion activée SSL/TLS |
Variables d'environnement PostgreSQL
Les variables d'environnement suivantes peuvent être utilisées pour définir la connexion PostgreSQL :
| Variable d'environnement | Description |
|---|---|
POSTGRES_URL | URL de connexion principale pour PostgreSQL |
DATABASE_URL | URL de connexion alternative (détectée automatiquement) |
PGURL | URL de connexion alternative |
PG_URL | URL de connexion alternative |
TLS_POSTGRES_DATABASE_URL | URL de connexion activée SSL/TLS |
TLS_DATABASE_URL | URL de connexion alternative activée SSL/TLS |
Si aucune URL de connexion n'est fournie, le système vérifie les paramètres individuels suivants :
| Variable d'environnement | Variables de repli | Valeur par défaut | Description |
|---|---|---|---|
PGHOST | - | localhost | Hôte de la base de données |
PGPORT | - | 5432 | Port de la base de données |
PGUSERNAME | PGUSER, USER, USERNAME | postgres | Utilisateur de la base de données |
PGPASSWORD | - | (vide) | Mot de passe de la base de données |
PGDATABASE | - | username | Nom de la base de données |
Variables d'environnement SQLite
Les connexions SQLite peuvent être configurées via DATABASE_URL lorsqu'elle contient une URL compatible SQLite :
# Tous sont reconnus comme SQLite
DATABASE_URL=":memory:"
DATABASE_URL="sqlite://./app.db"
DATABASE_URL="file:///absolute/path/to/db.sqlite"Note : Les variables d'environnement spécifiques à PostgreSQL (POSTGRES_URL, PGHOST, etc.) sont ignorées lors de l'utilisation de SQLite.
Préconnexion au moment de l'exécution
Bun peut pré-se connecter à PostgreSQL au démarrage pour améliorer les performances en établissant des connexions de base de données avant que le code de votre application ne s'exécute. Cela est utile pour réduire la latence de connexion lors de la première requête de base de données.
# Activer la préconnexion PostgreSQL
bun --sql-preconnect index.js
# Fonctionne avec la variable d'environnement DATABASE_URL
DATABASE_URL=postgres://user:pass@localhost:5432/db bun --sql-preconnect index.js
# Peut être combiné avec d'autres indicateurs d'exécution
bun --sql-preconnect --hot index.jsL'indicateur --sql-preconnect établira automatiquement une connexion PostgreSQL en utilisant vos variables d'environnement configurées au démarrage. Si la connexion échoue, cela ne plantera pas votre application - l'erreur sera gérée gracieusement.
Options de connexion
Vous pouvez configurer manuellement votre connexion de base de données en passant des options au constructeur SQL. Les options varient selon l'adaptateur de base de données :
Options MySQL
import { SQL } from "bun";
const sql = new SQL({
// Requis pour MySQL lors de l'utilisation d'un objet d'options
adapter: "mysql",
// Détails de connexion
hostname: "localhost",
port: 3306,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Connexion via socket Unix (alternative à hostname/port)
// socket: "/var/run/mysqld/mysqld.sock",
// Paramètres de pool de connexions
max: 20, // Nombre maximum de connexions dans le pool (par défaut : 10)
idleTimeout: 30, // Fermer les connexions inactives après 30s
maxLifetime: 0, // Durée de vie de la connexion en secondes (0 = pour toujours)
connectionTimeout: 30, // Délai d'expiration lors de l'établissement de nouvelles connexions
// Options SSL/TLS
ssl: "prefer", // ou "disable", "require", "verify-ca", "verify-full"
// tls: {
// rejectUnauthorized: true,
// ca: "path/to/ca.pem",
// key: "path/to/key.pem",
// cert: "path/to/cert.pem",
// },
// Rappels
onconnect: client => {
console.log("Connecté à MySQL");
},
onclose: (client, err) => {
if (err) {
console.error("Erreur de connexion MySQL :", err);
} else {
console.log("Connexion MySQL fermée");
}
},
});Options PostgreSQL
import { SQL } from "bun";
const sql = new SQL({
// Détails de connexion (l'adaptateur est détecté automatiquement comme PostgreSQL)
url: "postgres://user:pass@localhost:5432/dbname",
// Paramètres de connexion alternatifs
hostname: "localhost",
port: 5432,
database: "myapp",
username: "dbuser",
password: "secretpass",
// Paramètres de pool de connexions
max: 20, // Nombre maximum de connexions dans le pool
idleTimeout: 30, // Fermer les connexions inactives après 30s
maxLifetime: 0, // Durée de vie de la connexion en secondes (0 = pour toujours)
connectionTimeout: 30, // Délai d'expiration lors de l'établissement de nouvelles connexions
// Options 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) {
// ...
// },
// },
// Rappels
onconnect: client => {
console.log("Connecté à PostgreSQL");
},
onclose: client => {
console.log("Connexion PostgreSQL fermée");
},
});Options SQLite
import { SQL } from "bun";
const sql = new SQL({
// Requis pour SQLite
adapter: "sqlite",
filename: "./data/app.db", // ou ":memory:" pour une base de données en mémoire
// Modes d'accès spécifiques à SQLite
readonly: false, // Ouvrir en mode lecture seule
create: true, // Créer la base de données si elle n'existe pas
readwrite: true, // Autoriser les opérations de lecture et d'écriture
// Gestion des données SQLite
strict: true, // Activer le mode strict pour une meilleure sécurité des types
safeIntegers: false, // Utiliser BigInt pour les entiers dépassant la plage des nombres JS
// Rappels
onconnect: client => {
console.log("Base de données SQLite ouverte");
},
onclose: client => {
console.log("Base de données SQLite fermée");
},
});Notes sur les connexions SQLite">
- Mise en pool des connexions : SQLite n'utilise pas la mise en pool des connexions car il s'agit d'une base de données basée sur des fichiers. Chaque instance
SQLreprésente une seule connexion. - Transactions : SQLite prend en charge les transactions imbriquées via des points de sauvegarde, similaires à PostgreSQL.
- Accès concurrent : SQLite gère l'accès concurrent via le verrouillage de fichiers. Utilisez le mode WAL pour une meilleure concurrence.
- Bases de données en mémoire : L'utilisation de
:memory:crée une base de données temporaire qui n'existe que pendant la durée de vie de la connexion.
Mots de passe dynamiques
Lorsque les clients doivent utiliser des schémas d'authentification alternatifs tels que des jetons d'accès ou des connexions à des bases de données avec des mots de passe tournants, fournissez une fonction synchrone ou asynchrone qui résoudra la valeur du mot de passe dynamique au moment de la connexion.
import { SQL } from "bun";
const sql = new SQL(url, {
// Autre configuration de connexion
...
// Fonction de mot de passe pour l'utilisateur de la base de données
password: async () => await signer.getAuthToken(),
});Fonctionnalités spécifiques à SQLite
Exécution de requêtes
SQLite exécute les requêtes de manière synchrone, contrairement à PostgreSQL qui utilise des E/S asynchrones. Cependant, l'API reste cohérente en utilisant des promesses :
const sqlite = new SQL("sqlite://app.db");
// Fonctionne comme PostgreSQL, mais s'exécute de manière synchrone sous le capot
const users = await sqlite`SELECT * FROM users`;
// Les paramètres fonctionnent de manière identique
const user = await sqlite`SELECT * FROM users WHERE id = ${userId}`;Pragmas SQLite
Vous pouvez utiliser des instructions PRAGMA pour configurer le comportement de SQLite :
const sqlite = new SQL("sqlite://app.db");
// Activer les clés étrangères
await sqlite`PRAGMA foreign_keys = ON`;
// Définir le mode journal sur WAL pour une meilleure concurrence
await sqlite`PRAGMA journal_mode = WAL`;
// Vérifier l'intégrité
const integrity = await sqlite`PRAGMA integrity_check`;Différences de types de données
SQLite a un système de types plus flexible que PostgreSQL :
// SQLite stocke les données dans 5 classes de stockage : NULL, INTEGER, REAL, TEXT, BLOB
const sqlite = new SQL("sqlite://app.db");
// SQLite est plus indulgent avec les types
await sqlite`
CREATE TABLE flexible (
id INTEGER PRIMARY KEY,
data TEXT, -- Peut stocker des nombres sous forme de chaînes
value NUMERIC, -- Peut stocker des entiers, des réels ou du texte
blob BLOB -- Données binaires
)
`;
// Les valeurs JavaScript sont automatiquement converties
await sqlite`INSERT INTO flexible VALUES (${1}, ${"text"}, ${123.45}, ${Buffer.from("binary")})`;Transactions
Pour démarrer une nouvelle transaction, utilisez sql.begin. Cette méthode fonctionne à la fois pour PostgreSQL et SQLite. Pour PostgreSQL, elle réserve une connexion dédiée du pool. Pour SQLite, elle démarre une transaction sur la connexion unique.
La commande BEGIN est envoyée automatiquement, y compris toute configuration optionnelle que vous spécifiez. Si une erreur se produit pendant la transaction, un ROLLBACK est déclenché pour assurer la continuité du processus.
Transactions de base
await sql.begin(async tx => {
// Toutes les requêtes dans cette fonction s'exécutent dans une transaction
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`;
// La transaction est automatiquement validée si aucune erreur n'est lancée
// Annulée si une erreur se produit
});Il est également possible de piper les requêtes dans une transaction si nécessaire en renvoyant un tableau avec les requêtes depuis la fonction de rappel comme ceci :
await sql.begin(async tx => {
return [
tx`INSERT INTO users (name) VALUES (${"Alice"})`,
tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = 1`,
];
});Points de sauvegarde
Les points de sauvegarde en SQL créent des points de contrôle intermédiaires dans une transaction, permettant des retours en arrière partiels sans affecter l'ensemble de l'opération. Ils sont utiles dans les transactions complexes, permettant la récupération d'erreurs et le maintien de résultats cohérents.
await sql.begin(async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
await tx.savepoint(async sp => {
// Cette partie peut être annulée séparément
await sp`UPDATE users SET status = 'active'`;
if (someCondition) {
throw new Error("Retour au point de sauvegarde");
}
});
// Continuer avec la transaction même si le point de sauvegarde a été annulé
await tx`INSERT INTO audit_log (action) VALUES ('user_created')`;
});Transactions distribuées
Two-Phase Commit (2PC) est un protocole de transaction distribuée où la phase 1 implique que le coordinateur prépare les nœuds en s'assurant que les données sont écrites et prêtes à être validées, tandis que la phase 2 finalise avec les nœuds validant ou annulant en fonction de la décision du coordinateur. Ce processus assure la durabilité des données et une gestion appropriée des verrous.
Dans PostgreSQL et MySQL, les transactions distribuées persistent au-delà de leur session d'origine, permettant aux utilisateurs privilégiés ou aux coordinateurs de les valider ou de les annuler ultérieurement. Cela prend en charge les transactions distribuées robustes, les processus de récupération et les opérations administratives.
Chaque système de base de données implémente les transactions distribuées différemment :
PostgreSQL les prend en charge nativement via des transactions préparées, tandis que MySQL utilise les transactions XA.
Si des exceptions se produisent pendant la transaction distribuée et ne sont pas attrapées, le système annulera automatiquement toutes les modifications. Lorsque tout se déroule normalement, vous conservez la flexibilité de valider ou d'annuler la transaction ultérieurement.
// Démarrer une transaction distribuée
await sql.beginDistributed("tx1", async tx => {
await tx`INSERT INTO users (name) VALUES (${"Alice"})`;
});
// Plus tard, valider ou annuler
await sql.commitDistributed("tx1");
// ou
await sql.rollbackDistributed("tx1");Authentification
Bun prend en charge l'authentification SCRAM-SHA-256 (SASL), MD5 et texte clair. SASL est recommandé pour une meilleure sécurité. Consultez Authentification SASL Postgres pour plus d'informations.
Aperçu des modes SSL
PostgreSQL prend en charge différents modes SSL/TLS pour contrôler comment les connexions sécurisées sont établies. Ces modes déterminent le comportement lors de la connexion et le niveau de vérification du certificat effectué.
const sql = new SQL({
hostname: "localhost",
username: "user",
password: "password",
ssl: "disable", // | "prefer" | "require" | "verify-ca" | "verify-full"
});| Mode SSL | Description |
|---|---|
disable | Aucun SSL/TLS utilisé. Les connexions échouent si le serveur nécessite SSL. |
prefer | Essaie SSL en premier, revient à non-SSL si SSL échoue. Mode par défaut si aucun n'est spécifié. |
require | Nécessite SSL sans vérification du certificat. Échoue si SSL ne peut pas être établi. |
verify-ca | Vérifie que le certificat du serveur est signé par une CA de confiance. Échoue si la vérification échoue. |
verify-full | Mode le plus sécurisé. Vérifie que le certificat et le nom d'hôte correspondent. Protège contre les certificats non fiables et les attaques MITM. |
Utilisation avec les chaînes de connexion
Le mode SSL peut également être spécifié dans les chaînes de connexion :
// Utilisation du mode prefer
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=prefer");
// Utilisation du mode verify-full
const sql = new SQL("postgres://user:password@localhost/mydb?sslmode=verify-full");Mise en pool des connexions
Le client SQL de Bun gère automatiquement un pool de connexions, qui est un ensemble de connexions de base de données réutilisées pour plusieurs requêtes. Cela aide à réduire la surcharge d'établissement et de fermeture de connexions pour chaque requête, et aide également à gérer le nombre de connexions simultanées à la base de données.
const sql = new SQL({
// Configuration du pool
max: 20, // Maximum 20 connexions simultanées
idleTimeout: 30, // Fermer les connexions inactives après 30s
maxLifetime: 3600, // Durée de vie maximale de la connexion 1 heure
connectionTimeout: 10, // Délai d'expiration de connexion 10s
});Aucune connexion ne sera établie tant qu'une requête n'est pas faite.
const sql = Bun.SQL(); // aucune connexion n'est créée
await sql`...`; // le pool est démarré jusqu'à ce que max soit atteint (si possible), première connexion disponible est utilisée
await sql`...`; // la connexion précédente est réutilisée
// deux connexions sont utilisées maintenant en même temps
await Promise.all([
sql`INSERT INTO users ${sql({ name: "Alice" })}`,
sql`UPDATE users SET name = ${user.name} WHERE id = ${user.id}`,
]);
await sql.close(); // attendre que toutes les requêtes se terminent et fermer toutes les connexions du pool
await sql.close({ timeout: 5 }); // attendre 5 secondes et fermer toutes les connexions du pool
await sql.close({ timeout: 0 }); // fermer immédiatement toutes les connexions du poolConnexions réservées
Bun vous permet de réserver une connexion du pool et renvoie un client qui encapsule la connexion unique. Cela peut être utilisé pour exécuter des requêtes sur une connexion isolée.
// Obtenir une connexion exclusive du pool
const reserved = await sql.reserve();
try {
await reserved`INSERT INTO users (name) VALUES (${"Alice"})`;
} finally {
// Important : Relâcher la connexion au pool
reserved.release();
}
// Ou en utilisant Symbol.dispose
{
using reserved = await sql.reserve();
await reserved`SELECT 1`;
} // Automatiquement relâchéeInstructions préparées
Par défaut, le client SQL de Bun crée automatiquement des instructions préparées nommées pour les requêtes où il peut être déduit que la requête est statique. Cela offre de meilleures performances. Cependant, vous pouvez modifier ce comportement en définissant prepare: false dans les options de connexion :
const sql = new SQL({
// ... autres options ...
prepare: false, // Désactiver la persistance des instructions préparées nommées sur le serveur
});Lorsque prepare: false est défini :
Les requêtes sont toujours exécutées en utilisant le protocole "extended", mais elles sont exécutées en utilisant des instructions préparées non nommées, une instruction préparée non nommée ne dure que jusqu'à ce que la prochaine instruction Parse spécifiant l'instruction non nommée comme destination soit émise.
- La liaison de paramètres est toujours sûre contre les injections SQL
- Chaque requête est analysée et planifiée à partir de zéro par le serveur
- Les requêtes ne seront pas pipelinées
Vous voudrez peut-être utiliser prepare: false lorsque :
- Utilisation de PGBouncer en mode transaction (bien que depuis PGBouncer 1.21.0, les instructions préparées nommées au niveau du protocole soient prises en charge lorsqu'elles sont configurées correctement)
- Débogage des plans d'exécution de requêtes
- Travail avec du SQL dynamique où les plans de requêtes doivent être régénérés fréquemment
- Plus d'une commande par requête ne sera pas prise en charge (sauf si vous utilisez
sql``.simple())
Notez que la désactivation des instructions préparées peut avoir un impact sur les performances pour les requêtes exécutées fréquemment avec différents paramètres, car le serveur doit analyser et planifier chaque requête à partir de zéro.
Gestion des erreurs
Le client fournit des erreurs typées pour différents scénarios d'échec. Les erreurs sont spécifiques à la base de données et étendent les classes d'erreurs de base :
Classes d'erreurs
import { SQL } from "bun";
try {
await sql`SELECT * FROM users`;
} catch (error) {
if (error instanceof SQL.PostgresError) {
// Erreur spécifique à PostgreSQL
console.log(error.code); // Code d'erreur PostgreSQL
console.log(error.detail); // Message d'erreur détaillé
console.log(error.hint); // Indice utile de PostgreSQL
} else if (error instanceof SQL.SQLiteError) {
// Erreur spécifique à SQLite
console.log(error.code); // Code d'erreur SQLite (par exemple, "SQLITE_CONSTRAINT")
console.log(error.errno); // Numéro d'erreur SQLite
console.log(error.byteOffset); // Offset d'octet dans l'instruction SQL (si disponible)
} else if (error instanceof SQL.SQLError) {
// Erreur SQL générique (classe de base)
console.log(error.message);
}
}Codes d'erreur spécifiques à PostgreSQL">
Erreurs de connexion PostgreSQL
| Erreurs de connexion | Description |
|---|---|
ERR_POSTGRES_CONNECTION_CLOSED | La connexion a été terminée ou n'a jamais été établie |
ERR_POSTGRES_CONNECTION_TIMEOUT | Échec de l'établissement de la connexion dans le délai imparti |
ERR_POSTGRES_IDLE_TIMEOUT | Connexion fermée pour cause d'inactivité |
ERR_POSTGRES_LIFETIME_TIMEOUT | La connexion a dépassé la durée de vie maximale |
ERR_POSTGRES_TLS_NOT_AVAILABLE | Connexion SSL/TLS non disponible |
ERR_POSTGRES_TLS_UPGRADE_FAILED | Échec de la mise à niveau de la connexion vers SSL/TLS |
Erreurs d'authentification
| Erreurs d'authentification | Description |
|---|---|
ERR_POSTGRES_AUTHENTICATION_FAILED_PBKDF2 | Échec de l'authentification par mot de passe |
ERR_POSTGRES_UNKNOWN_AUTHENTICATION_METHOD | Le serveur a demandé une méthode d'authentification inconnue |
ERR_POSTGRES_UNSUPPORTED_AUTHENTICATION_METHOD | Le serveur a demandé une méthode d'authentification non prise en charge |
ERR_POSTGRES_INVALID_SERVER_KEY | Clé de serveur invalide pendant l'authentification |
ERR_POSTGRES_INVALID_SERVER_SIGNATURE | Signature de serveur invalide |
ERR_POSTGRES_SASL_SIGNATURE_INVALID_BASE64 | Encodage de signature SASL invalide |
ERR_POSTGRES_SASL_SIGNATURE_MISMATCH | Échec de la vérification de signature SASL |
Erreurs de requête
| Erreurs de requête | Description |
|---|---|
ERR_POSTGRES_SYNTAX_ERROR | Syntaxe SQL invalide (étend SyntaxError) |
ERR_POSTGRES_SERVER_ERROR | Erreur générale du serveur PostgreSQL |
ERR_POSTGRES_INVALID_QUERY_BINDING | Liaison de paramètres invalide |
ERR_POSTGRES_QUERY_CANCELLED | La requête a été annulée |
ERR_POSTGRES_NOT_TAGGED_CALL | La requête a été appelée sans appel tagué |
Erreurs de type de données
| Erreurs de type de données | Description |
|---|---|
ERR_POSTGRES_INVALID_BINARY_DATA | Format de données binaires invalide |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE | Séquence d'octets invalide |
ERR_POSTGRES_INVALID_BYTE_SEQUENCE_FOR_ENCODING | Erreur d'encodage |
ERR_POSTGRES_INVALID_CHARACTER | Caractère invalide dans les données |
ERR_POSTGRES_OVERFLOW | Dépassement numérique |
ERR_POSTGRES_UNSUPPORTED_BYTEA_FORMAT | Format binaire non pris en charge |
ERR_POSTGRES_UNSUPPORTED_INTEGER_SIZE | Taille d'entier non prise en charge |
ERR_POSTGRES_MULTIDIMENSIONAL_ARRAY_NOT_SUPPORTED_YET | Tableaux multidimensionnels non pris en charge |
ERR_POSTGRES_NULLS_IN_ARRAY_NOT_SUPPORTED_YET | Valeurs NULL dans les tableaux non prises en charge |
Erreurs de protocole
| Erreurs de protocole | Description |
|---|---|
ERR_POSTGRES_EXPECTED_REQUEST | Requête client attendue |
ERR_POSTGRES_EXPECTED_STATEMENT | Instruction préparée attendue |
ERR_POSTGRES_INVALID_BACKEND_KEY_DATA | Données de clé backend invalides |
ERR_POSTGRES_INVALID_MESSAGE | Message de protocole invalide |
ERR_POSTGRES_INVALID_MESSAGE_LENGTH | Longueur de message invalide |
ERR_POSTGRES_UNEXPECTED_MESSAGE | Type de message inattendu |
Erreurs de transaction
| Erreurs de transaction | Description |
|---|---|
ERR_POSTGRES_UNSAFE_TRANSACTION | Opération de transaction non sécurisée détectée |
ERR_POSTGRES_INVALID_TRANSACTION_STATE | État de transaction invalide |
Erreurs spécifiques à SQLite
Les erreurs SQLite fournissent des codes d'erreur et des numéros qui correspondent aux codes d'erreur standard de SQLite :
Codes d'erreur SQLite courants">
| Code d'erreur | errno | Description |
|---|---|---|
SQLITE_CONSTRAINT | 19 | Violation de contrainte (UNIQUE, CHECK, NOT NULL, etc.) |
SQLITE_BUSY | 5 | La base de données est verrouillée |
SQLITE_LOCKED | 6 | La table dans la base de données est verrouillée |
SQLITE_READONLY | 8 | Tentative d'écriture dans une base de données en lecture seule |
SQLITE_IOERR | 10 | Erreur d'E/S disque |
SQLITE_CORRUPT | 11 | L'image disque de la base de données est corrompue |
SQLITE_FULL | 13 | La base de données ou le disque est plein |
SQLITE_CANTOPEN | 14 | Impossible d'ouvrir le fichier de base de données |
SQLITE_PROTOCOL | 15 | Erreur de protocole de verrouillage de base de données |
SQLITE_SCHEMA | 17 | Le schéma de la base de données a changé |
SQLITE_TOOBIG | 18 | La chaîne ou BLOB dépasse la limite de taille |
SQLITE_MISMATCH | 20 | Incompatibilité de type de données |
SQLITE_MISUSE | 21 | Bibliothèque utilisée incorrectement |
SQLITE_AUTH | 23 | Autorisation refusée |
Exemple de gestion des erreurs :
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 en double
} catch (error) {
if (error instanceof SQL.SQLiteError) {
if (error.code === "SQLITE_CONSTRAINT") {
console.log("Violation de contrainte :", error.message);
// Gérer la violation de contrainte unique
}
}
}Nombres et BigInt
Le client SQL de Bun inclut une gestion spéciale pour les grands nombres qui dépassent la plage d'un entier 53 bits. Voici comment cela fonctionne :
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 au lieu de chaînes
Si vous avez besoin de grands nombres sous forme de BigInt au lieu de chaînes, vous pouvez activer cela en définissant l'option bigint sur true lors de l'initialisation du client SQL :
const sql = new SQL({
bigint: true,
});
const [{ x }] = await sql`SELECT 9223372036854777 as x`;
console.log(typeof x, x); // "bigint" 9223372036854777nFeuille de route
Il reste encore certaines choses que nous n'avons pas terminées.
- Préchargement de connexion via l'indicateur CLI
--db-preconnectde Bun - Transformations de noms de colonnes (par exemple
snake_caseverscamelCase). Cela est principalement bloqué sur une implémentation consciente d'unicode du changement de casse en C++ utilisantWTF::Stringde WebKit. - Transformations de types de colonnes
Fonctionnalités spécifiques aux bases de données
Méthodes d'authentification
MySQL prend en charge plusieurs plugins d'authentification qui sont automatiquement négociés :
mysql_native_password- Authentification MySQL traditionnelle, largement compatiblecaching_sha2_password- Par défaut dans MySQL 8.0+, plus sécurisé avec échange de clés RSAsha256_password- Authentification basée sur SHA-256
Le client gère automatiquement le changement de plugin d'authentification lorsque demandé par le serveur, y compris l'échange sécurisé de mots de passe sur des connexions non-SSL.
Instructions préparées et performances
MySQL utilise des instructions préparées côté serveur pour toutes les requêtes paramétrées :
// Cela crée automatiquement une instruction préparée sur le serveur
const user = await mysql`SELECT * FROM users WHERE id = ${userId}`;
// Les instructions préparées sont mises en cache et réutilisées pour des requêtes identiques
for (const id of userIds) {
// La même instruction préparée est réutilisée
await mysql`SELECT * FROM users WHERE id = ${id}`;
}
// Pipelining de requêtes - plusieurs instructions envoyées sans attendre
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}`,
]);Jeux de résultats multiples
MySQL peut renvoyer plusieurs jeux de résultats à partir de requêtes multi-instructions :
const mysql = new SQL("mysql://user:pass@localhost/mydb");
// Requêtes multi-instructions avec la méthode simple()
const multiResults = await mysql`
SELECT * FROM users WHERE id = 1;
SELECT * FROM orders WHERE user_id = 1;
`.simple();Jeux de caractères et collationnements
Bun.SQL utilise automatiquement le jeu de caractères utf8mb4 pour les connexions MySQL, assurant une prise en charge complète d'Unicode y compris les emojis. C'est le jeu de caractères recommandé pour les applications MySQL modernes.
Attributs de connexion
Bun envoie automatiquement des informations client à MySQL pour une meilleure surveillance :
// Ces attributs sont envoyés automatiquement :
// _client_name: "Bun"
// _client_version: <version de bun>
// Vous pouvez les voir dans performance_schema.session_connect_attrs de MySQLGestion des types
Les types MySQL sont automatiquement convertis en types JavaScript :
| Type MySQL | Type JavaScript | Notes |
|---|---|---|
| INT, TINYINT, MEDIUMINT | number | Dans la plage d'entiers sûrs |
| BIGINT | string, number ou BigInt | Si la valeur tient dans i32/u32 la taille sera number sinon string ou BigInt selon l'option bigint |
| DECIMAL, NUMERIC | string | Pour préserver la précision |
| FLOAT, DOUBLE | number | |
| DATE | Date | Objet Date JavaScript |
| DATETIME, TIMESTAMP | Date | Avec gestion du fuseau horaire |
| TIME | number | Total de microsecondes |
| YEAR | number | |
| CHAR, VARCHAR, VARSTRING, STRING | string | |
| TINY TEXT, MEDIUM TEXT, TEXT, LONG TEXT | string | |
| TINY BLOB, MEDIUM BLOB, BLOG, LONG BLOB | string | Les types BLOB sont des alias pour les types TEXT |
| JSON | object/array | Automatiquement analysé |
| BIT(1) | boolean | BIT(1) dans MySQL |
| GEOMETRY | string | Données géométriques |
Différences par rapport à PostgreSQL
Bien que l'API soit unifiée, il y a quelques différences de comportement :
- Espaces réservés de paramètres : MySQL utilise
?en interne mais Bun convertit automatiquement le style$1, $2 - Clause RETURNING : MySQL ne prend pas en charge RETURNING ; utilisez
result.lastInsertRowidou un SELECT séparé - Types de tableaux : MySQL n'a pas de types de tableaux natifs comme PostgreSQL
Fonctionnalités spécifiques à MySQL
Nous n'avons pas encore implémenté la prise en charge de LOAD DATA INFILE
Fonctionnalités spécifiques à PostgreSQL
Nous n'avons pas encore implémenté :
- Prise en charge de
COPY - Prise en charge de
LISTEN - Prise en charge de
NOTIFY
Nous n'avons pas non plus implémenté certaines des fonctionnalités les moins courantes comme :
- Authentification GSSAPI
- Prise en charge de
SCRAM-SHA-256-PLUS - Types Point et PostGIS
- Tous les types de tableaux d'entiers multidimensionnels (seuls quelques types sont pris en charge)
Modèles courants et meilleures pratiques
Travailler avec des jeux de résultats MySQL
// Obtenir l'ID d'insertion après INSERT
const result = await mysql`INSERT INTO users (name) VALUES (${"Alice"})`;
console.log(result.lastInsertRowid); // LAST_INSERT_ID() de MySQL
// Gérer les lignes affectées
const updated = await mysql`UPDATE users SET active = ${false} WHERE age < ${18}`;
console.log(updated.affectedRows); // Nombre de lignes mises à jour
// Utilisation de fonctions spécifiques à MySQL
const now = await mysql`SELECT NOW() as current_time`;
const uuid = await mysql`SELECT UUID() as id`;Gestion des erreurs MySQL
try {
await mysql`INSERT INTO users (email) VALUES (${"duplicate@email.com"})`;
} catch (error) {
if (error.code === "ER_DUP_ENTRY") {
console.log("Entrée en double détectée");
} else if (error.code === "ER_ACCESS_DENIED_ERROR") {
console.log("Accès refusé");
} else if (error.code === "ER_BAD_DB_ERROR") {
console.log("La base de données n'existe pas");
}
// Les codes d'erreur MySQL sont compatibles avec les packages mysql/mysql2
}Conseils de performance pour MySQL
- Utiliser la mise en pool des connexions : Définir une taille de pool
maxappropriée en fonction de votre charge de travail - Activer les instructions préparées : Elles sont activées par défaut et améliorent les performances
- Utiliser des transactions pour les opérations en bloc : Grouper les requêtes liées dans des transactions
- Indexer correctement : MySQL repose fortement sur les index pour les performances des requêtes
- Utiliser le jeu de caractères
utf8mb4: Il est défini par défaut et gère tous les caractères Unicode
Foire aux questions
Pourquoi est-ce `Bun.sql` et pas `Bun.postgres` ?
Le plan était d'ajouter plus de pilotes de base de données à l'avenir. Maintenant avec la prise en charge de MySQL ajoutée, cette API unifiée prend en charge PostgreSQL, MySQL et SQLite.
Comment savoir quel adaptateur de base de données est utilisé ?
L'adaptateur est automatiquement détecté depuis la chaîne de connexion :
- Les URLs commençant par `mysql://` ou `mysql2://` utilisent MySQL
- Les URLs correspondant aux modèles SQLite (`:memory:`, `sqlite://`, `file://`) utilisent SQLite
- Tout le reste utilise PostgreSQL par défaut
Les procédures stockées MySQL sont-elles prises en charge ?">
Oui, les procédures stockées sont entièrement prises en charge y compris les paramètres OUT et les jeux de résultats multiples :
```ts
// Appeler une procédure stockée
const results = await mysql`CALL GetUserStats(${userId}, @total_orders)`;
// Obtenir le paramètre OUT
const outParam = await mysql`SELECT @total_orders as total`;
```
Puis-je utiliser une syntaxe SQL spécifique à MySQL ?">
Oui, vous pouvez utiliser n'importe quelle syntaxe spécifique à MySQL :
```ts
// La syntaxe spécifique à MySQL fonctionne bien
await mysql`SET @user_id = ${userId}`;
await mysql`SHOW TABLES`;
await mysql`DESCRIBE users`;
await mysql`EXPLAIN SELECT * FROM users WHERE id = ${id}`;
```
Pourquoi ne pas simplement utiliser une bibliothèque existante ?
Les packages npm comme postgres.js, pg et node-postgres peuvent également être utilisés dans Bun. Ce sont d'excellentes options.
Deux raisons pour lesquelles :
- Nous pensons qu'il est plus simple pour les développeurs d'avoir un pilote de base de données intégré dans Bun. Le temps que vous passez à chercher des bibliothèques est du temps que vous pourriez passer à construire votre application.
- Nous exploitons certains internes du moteur JavaScriptCore pour rendre plus rapide la création d'objets qui seraient difficiles à implémenter dans une bibliothèque
Crédits
Un grand merci à @porsager pour postgres.js pour l'inspiration de l'interface API.