Connect Node.js to a database
A Node.js app without a database is rare: accounts, orders, content. On app hosting, the database and its access depend on the plan; the code must read host, port, name and credentials from panel variables. Two decisions matter: the driver and connection pooling.
Choose the driver
| Database | Recommended package | Note |
|---|---|---|
| MySQL / MariaDB | mysql2 |
Native promises, the mysql package is obsolete |
| PostgreSQL | pg |
Built-in pool |
| Either through an ORM | Prisma, Sequelize, Knex | More abstraction, migrations included |
A driver is enough for a small API. An ORM helps with several tables, migrations and a team. In both cases, queries must be parameterised.
Configure without hard-coding
DATABASE_HOST=...
DATABASE_PORT=3306
DATABASE_NAME=...
DATABASE_USER=...
DATABASE_PASSWORD=...or a single URL:
DATABASE_URL=mysql://user:password@host:3306/databaseUse the host shown in the panel, not localhost by reflex: the database may run on another machine than the process. These values never belong in Git. Validate their presence at startup and log the host and database name, never the password.
Pool and queries
Opening a connection per request wastes resources and adds latency. Use one pool, created at startup.
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: process.env.DATABASE_HOST,
port: Number(process.env.DATABASE_PORT || 3306),
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
waitForConnections: true,
connectionLimit: 5,
queueLimit: 0
});
const [rows] = await pool.query('SELECT id, name FROM items WHERE id = ?', [id]);With PostgreSQL:
const { Pool } = require('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 5 });
const { rows } = await pool.query('SELECT id, name FROM items WHERE id = $1', [id]);Size connectionLimit or max to the plan: a pool of 20 against a database that allows 10 causes exhaustion errors. On Too many connections, reduce the pool before upgrading the plan. Close the pool on SIGTERM with await pool.end().
Migrations and backups
A migration must be versioned, idempotent and run once. Do not run it automatically on every start if several instances may boot in parallel. Back up the database before a destructive migration, then test the restore.
The application user needs neither DROP nor administrative rights. Grant only SELECT, INSERT, UPDATE, DELETE on the relevant database. SQL exports must not end up in a public folder.
Check
Run SELECT 1, then a real query on a table. Confirm the log shows the right database, connection errors are explicit and the pool closes on restart. If the app starts but the first query fails, the problem is usually the host or permissions, not the driver.
Advertisement