Connect Node.js to a database

2 min read07 November 2026

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

Bash
DATABASE_HOST=...
DATABASE_PORT=3306
DATABASE_NAME=...
DATABASE_USER=...
DATABASE_PASSWORD=...

or a single URL:

Bash
DATABASE_URL=mysql://user:password@host:3306/database

Use 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.

JavaScript
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:

JavaScript
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

Was this article helpful?

Read next