MySQL with Node.js
MySQL (and its drop-in fork MariaDB) is the other dominant open-source relational database. The modern driver for Node is mysql2 — faster than the original mysql package and, crucially, promise-capable. Most concepts carry over directly from Postgres: a pool, parameterized queries, transactions. This page highlights what's the same and the MySQL-specific differences that trip people up.
Use `mysql2`, and its promise API
npm install mysql2
db.js
// Import the PROMISE build so you can await queries:
import mysql from 'mysql2/promise'
export const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
connectionLimit: 10,
waitForConnections: true,
})Parameterized queries — `?` placeholders
// MySQL uses ? placeholders (Postgres uses $1, $2): const [rows] = await pool.query( 'SELECT id, name FROM users WHERE email = ? AND active = ?', [req.body.email, true], ) // query() returns [rows, fields] — destructure the first element: const user = rows[0]
`query` vs `execute` (prepared statements)
// query() — sends SQL each time; placeholders escaped client-side.
const [a] = await pool.query('SELECT * FROM users WHERE id = ?', [id])
// execute() — uses a PREPARED statement (parsed once, reused); slightly safer/faster
// for repeated queries:
const [b] = await pool.execute('SELECT * FROM users WHERE id = ?', [id])Writes — `insertId` and `affectedRows`
const [result] = await pool.execute(
'INSERT INTO users (name, email) VALUES (?, ?)',
['Ada', 'ada@x.com'],
)
result.insertId // the new AUTO_INCREMENT id
result.affectedRows // rows inserted/updated/deleted
const [upd] = await pool.execute('UPDATE users SET name = ? WHERE id = ?', ['Ada L.', 1])
if (upd.affectedRows === 0) { /* no such row → 404 */ }Transactions
const conn = await pool.getConnection()
try {
await conn.beginTransaction()
await conn.execute('UPDATE accounts SET balance = balance - ? WHERE id = ?', [100, 1])
await conn.execute('UPDATE accounts SET balance = balance + ? WHERE id = ?', [100, 2])
await conn.commit()
} catch (err) {
await conn.rollback()
throw err
} finally {
conn.release() // return the connection to the pool
}MySQL gotchas
Topic | Watch out for |
|---|---|
Storage engine | Use InnoDB (default) — MyISAM has no transactions/FKs |
Charset | Use |
Booleans |
|
Dates |
|
Decimals | Use |
Postgres or MySQL?
Both are mature, fast, free, and well-supported in Node — you can build anything on either.
Postgres leans richer:
JSONB, arrays, stricter SQL compliance, advanced indexing,RETURNING.MySQL/MariaDB is ubiquitous in shared hosting and the LAMP world; very fast for simple read-heavy loads.