Connexion et utilisation de MySQL Node.js
MySQL est l'une des bases de données relationnelles open source les plus populaires, et Node.js est un environnement d'exécution JavaScript basé sur le moteur Chrome V8. Combiner les deux vous permet de créer des services backend puissants.
Pourquoi choisir MySQL + Node.js ?
- MySQL offre un stockage et une gestion fiables des données
- Le modèle d'E/S non bloquant de Node.js convient aux opérations de base de données
- Développement full-stack en JavaScript (en utilisant le même langage pour le frontend et le backend)
- Le riche écosystème npm propose de nombreux paquets liés à MySQL
Installation des dépendances requises
Avant de commencer, nous devons installer lemysql2paquet, qui est un choix populaire pour se connecter à MySQL dans Node.js.
npm install mysql2
Pourquoi choisir mysql2 plutôt que mysql ?
- Meilleures performances
- Prend en charge l'API Promise
- Prend en charge les requêtes préparées
- Maintenance active
Établissement de la connexion à la base de données
Configuration de connexion de base
Exemple
const mysql = require('mysql2');
// Créer un pool de connexions (recommandé pour la production)
const pool = mysql.createPool({
host: 'localhost', // Adresse du serveur de base de données
user: 'root', // Nom d'utilisateur de la base de données
password: 'password', // Mot de passe de la base de données
database: 'test_db', // Nom de la base de données à laquelle se connecter
waitForConnections: true,
connectionLimit: 10, // Nombre maximum de connexions dans le pool
queueLimit: 0
});
// Obtenir une connexion basée sur les promesses
const promisePool = pool.promise();
// Créer un pool de connexions (recommandé pour la production)
const pool = mysql.createPool({
host: 'localhost', // Adresse du serveur de base de données
user: 'root', // Nom d'utilisateur de la base de données
password: 'password', // Mot de passe de la base de données
database: 'test_db', // Nom de la base de données à laquelle se connecter
waitForConnections: true,
connectionLimit: 10, // Nombre maximum de connexions dans le pool
queueLimit: 0
});
// Obtenir une connexion basée sur les promesses
const promisePool = pool.promise();
Pool de connexions vs connexion unique
Avantages d'un pool de connexions :
- Réutiliser les connexions, réduire la surcharge
- Gérer automatiquement le cycle de vie des connexions
- Éviter les fuites de connexions
- Meilleures performances
Scénarios adaptés à une connexion unique :
- Scripts simples
- Environnement de test
- Applications à faible concurrence
Effectuer des opérations CRUD de base
Interroger des données (SELECT)
Exemple
async function getUsers() {
try {
const [rows, fields] = await promisePool.query('SELECT * FROM users');
console.log(lignes);
return rows;
} catch (err) {
console.error('Erreur de requête :', err);
throw err;
}
}
try {
const [rows, fields] = await promisePool.query('SELECT * FROM users');
console.log(lignes);
return rows;
} catch (err) {
console.error('Erreur de requête :', err);
throw err;
}
}
Insertion de données (INSERT)
Exemple
async function addUser(user) {
try {
const [result] = await promisePool.query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[user.name, user.email]
);
console.log('ID d’insertion :', result.insertId);
return result;
} catch (err) {
console.error('Erreur d’insertion :', err);
throw err;
}
}
try {
const [result] = await promisePool.query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[user.name, user.email]
);
console.log('ID d’insertion :', result.insertId);
return result;
} catch (err) {
console.error('Erreur d’insertion :', err);
throw err;
}
}
Mise à jour des données (UPDATE)
Exemple
async function updateUser(id, updates) {
try {
const [result] = await promisePool.query(
'UPDATE users SET name = ?, email = ? WHERE id = ?',
[updates.name, updates.email, id]
);
console.log('Lignes affectées:', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur de mise à jour:', err);
throw err;
}
}
try {
const [result] = await promisePool.query(
'UPDATE users SET name = ?, email = ? WHERE id = ?',
[updates.name, updates.email, id]
);
console.log('Lignes affectées:', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur de mise à jour:', err);
throw err;
}
}
Suppression de données (DELETE)
Exemple
async function deleteUser(id) {
try {
const [result] = await promisePool.query(
'DELETE FROM users WHERE id = ?',
[id]
);
console.log('Lignes supprimées:', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur de suppression:', err);
throw err;
}
}
try {
const [result] = await promisePool.query(
'DELETE FROM users WHERE id = ?',
[id]
);
console.log('Lignes supprimées:', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur de suppression:', err);
throw err;
}
}
Fonctionnalités avancées et bonnes pratiques
Gestion des transactions
Exemple
async function transferFunds(fromId, toId, amount) {
let connection;
try {
// Obtenir une connexion depuis le pool
connection = await promisePool.getConnection();
// Démarrer une transaction
await connection.beginTransaction();
// Effectuer l'opération de transfert
await connection.query(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
[amount, fromId]
);
await connection.query(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
[amount, toId]
);
// Valider la transaction
await connection.commit();
console.log('Transfert réussi');
} catch (err) {
// Annuler en cas d'erreur
if (connection) await connection.rollback();
console.error('Échec du transfert :', err);
throw err;
} finally {
// Rendre la connexion au pool
if (connection) connection.release();
}
}
let connection;
try {
// Obtenir une connexion depuis le pool
connection = await promisePool.getConnection();
// Démarrer une transaction
await connection.beginTransaction();
// Effectuer l'opération de transfert
await connection.query(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
[amount, fromId]
);
await connection.query(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
[amount, toId]
);
// Valider la transaction
await connection.commit();
console.log('Transfert réussi');
} catch (err) {
// Annuler en cas d'erreur
if (connection) await connection.rollback();
console.error('Échec du transfert :', err);
throw err;
} finally {
// Rendre la connexion au pool
if (connection) connection.release();
}
}
Requêtes préparées
Les requêtes préparées peuvent améliorer les performances et prévenir l'injection SQL :
Exemple
async function getUserById(id) {
try {
// Préparer une requête préparée
const [rows] = await promisePool.execute(
'SELECT * FROM users WHERE id = ?',
[id]
);
return rows[0];
} catch (err) {
console.error('Erreur de requête :', err);
throw err;
}
}
try {
// Préparer une requête préparée
const [rows] = await promisePool.execute(
'SELECT * FROM users WHERE id = ?',
[id]
);
return rows[0];
} catch (err) {
console.error('Erreur de requête :', err);
throw err;
}
}
Écoute des événements du pool de connexions
Exemple
pool.on('connection', (connection) => {
console.log('Nouvelle connexion établie');
});
pool.on('acquire', (connection) => {
console.log('Connexion acquise');
});
pool.on('release', (connection) => {
console.log('Connexion libérée');
});
pool.on('enqueue', () => {
console.log('En attente d'une connexion disponible');
});
console.log('Nouvelle connexion établie');
});
pool.on('acquire', (connection) => {
console.log('Connexion acquise');
});
pool.on('release', (connection) => {
console.log('Connexion libérée');
});
pool.on('enqueue', () => {
console.log('En attente d'une connexion disponible');
});
Gestion des erreurs et débogage
Types d'erreurs courantes
- Erreurs de connexion: serveur de base de données inaccessible, échec d'authentification, etc.
- Erreurs de syntaxe de requête: instructions SQL incorrectes
- Violations de contraintes: telles que des clés primaires en double, des contraintes de clé étrangère, etc.
- Erreurs de délai d'attente: le temps d'exécution de la requête est trop long
Stratégies de gestion des erreurs
Exemple
async function safeQuery(sql, params) {
try {
const [rows] = await promisePool.query(sql, params);
return rows;
} catch (err) {
// Prendre différentes mesures en fonction du type d'erreur
switch (err.code) {
case 'ER_DUP_ENTRY':
console.warn('Entrée en double :', err.sqlMessage);
throw new Error('Les données existent déjà');
case 'ECONNREFUSED':
console.error('Impossible de se connecter à la base de données');
throw new Error('Service indisponible, veuillez réessayer plus tard');
default:
console.error('Erreur de base de données :', err);
throw err;
}
}
}
try {
const [rows] = await promisePool.query(sql, params);
return rows;
} catch (err) {
// Prendre différentes mesures en fonction du type d'erreur
switch (err.code) {
case 'ER_DUP_ENTRY':
console.warn('Entrée en double :', err.sqlMessage);
throw new Error('Les données existent déjà');
case 'ECONNREFUSED':
console.error('Impossible de se connecter à la base de données');
throw new Error('Service indisponible, veuillez réessayer plus tard');
default:
console.error('Erreur de base de données :', err);
throw err;
}
}
}
Conseils d'optimisation des performances
- Définissez correctement la taille du pool de connexions: généralement 2-3 fois le nombre de cœurs de CPU
- Utilisez un pool de connexions au lieu d'une connexion unique: en particulier dans les applications web
- Utilisez les index de manière appropriée: accélérez les performances des requêtes
- Opérations par lots: réduisez les allers-retours
- Utilisez des requêtes préparées: améliorez les performances des requêtes répétées
- Libérez régulièrement les ressources: évitez les fuites de connexion
Exemple d'insertion par lots :
Exemple
async function batchInsertUsers(users) {
const values = users.map(user => [user.name, user.email]);
try {
const [result] = await promisePool.query(
'INSERT INTO users (name, email) VALUES ?',
[values]
);
console.log('Lignes insérées :', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur d'insertion par lots :', err);
throw err;
}
}
const values = users.map(user => [user.name, user.email]);
try {
const [result] = await promisePool.query(
'INSERT INTO users (name, email) VALUES ?',
[values]
);
console.log('Lignes insérées :', result.affectedRows);
return result;
} catch (err) {
console.error('Erreur d'insertion par lots :', err);
throw err;
}
}
Considérations de sécurité
- Ne concaténez jamais les chaînes SQL: utilisez des requêtes paramétrées pour éviter les injections SQL
- Limitez les permissions des utilisateurs de la base de données: les comptes d'application n'ont besoin que des permissions nécessaires
- Chiffrez les données sensibles: par exemple, les mots de passe doivent être stockés avec des hachages salés
- Utilisez des connexions SSL: les connexions chiffrées sont recommandées en production
- Mettez régulièrement à jour les dépendances: maintenez le paquet mysql2 à jour
Structure complète du projet d'exemple
project/ ├── config/ │ └── db.js # 数据库配置 ├── models/ │ └── userModel.js # 数据模型 ├── services/ │ └── userService.js # 业务逻辑 ├── app.js # 主应用文件 └── package.json
exemple de db.js :
Exemple
const mysql = require('mysql2');
const pool = mysql.createPool({
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'test_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool.promise();
const pool = mysql.createPool({
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '',
database: process.env.DB_NAME || 'test_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool.promise();