Skip to content

Node.js and Databases

Almost every real backend needs to persist data somewhere. This lesson introduces how Node.js applications connect to databases before the dedicated MongoDB, Mongoose, MySQL, and PostgreSQL lessons that follow.

SQL vs NoSQL in the Node.js Ecosystem

SQL (relational) databases like PostgreSQL and MySQL organize data into tables with fixed schemas and relationships enforced by the database itself, ideal when data integrity and complex relationships matter. NoSQL databases like MongoDB store flexible, often JSON-like documents, ideal when your schema evolves quickly or naturally maps to nested objects.

Node.js doesn't favor either category at the runtime level, both are accessed through npm packages: official or community-maintained drivers (pg, mysql2, mongodb) or higher-level tools (ORMs/ODMs like Prisma, Sequelize, or Mongoose) that add convenience on top of a raw driver.

// Raw driver example (PostgreSQL)
import { Client } from 'pg';

const client = new Client({ connectionString: process.env.DATABASE_URL });
await client.connect();

const result = await client.query('SELECT * FROM users WHERE id = $1', [1]);
console.log(result.rows[0]);

Every database driver follows a similar shape: connect once, then run parameterized queries (never string-concatenated ones) against that connection.

Common Database Packages

pg          // PostgreSQL driver
mysql2       // MySQL driver
mongodb      // MongoDB official driver
mongoose     // MongoDB ODM (schemas, validation)
prisma       // Type-safe ORM for SQL databases
  • Drivers give you raw query access; ORMs/ODMs add schemas, validation, and query-building conveniences.
  • Always use parameterized queries ($1, ? placeholders) to prevent SQL injection.
  • Store connection strings in environment variables, never hardcode credentials.
  • Most drivers support connection pooling, reusing connections instead of opening a new one per request.

Database Options Cheatsheet

A quick comparison of common choices in the Node.js ecosystem.

Database Type Common Package
PostgreSQL SQL / relational pg
MySQL SQL / relational mysql2
MongoDB NoSQL / document mongodb, mongoose
SQLite SQL / embedded node:sqlite (built-in) or better-sqlite3
Redis In-memory key-value ioredis, redis

Why Connection Pooling Matters

Opening a fresh database connection for every request is slow and resource-intensive, establishing a connection involves a network round trip and authentication. A connection pool keeps a set of open connections ready to reuse, dramatically reducing per-request overhead under load.

import { Pool } from 'pg';

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10, // maximum connections in the pool
});

app.get('/users/:id', async (req, res) => {
  const result = await pool.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
  res.json(result.rows[0]);
});

The pool automatically hands out an available connection per query and returns it to the pool afterward, you rarely manage individual connections directly.

Raw Driver vs ORM/ODM

Raw drivers give you full control over queries but require writing SQL (or MongoDB query syntax) by hand. ORMs/ODMs like Prisma, Sequelize, or Mongoose add schema definitions, validation, and often a more JavaScript-native query API, at the cost of an additional abstraction layer to learn.

  • Choose a raw driver for maximum control and minimal dependencies on smaller projects.
  • Choose an ORM/ODM when schema validation and developer convenience outweigh the extra abstraction.
  • Either way, always parameterize queries and never concatenate user input into query strings.

Common Mistakes

  • Opening a new database connection per request instead of using a connection pool.
  • Concatenating user input directly into a SQL query string, opening the door to SQL injection.
  • Hardcoding database credentials in source code instead of environment variables.
  • Choosing a NoSQL database purely for trendiness when the data is naturally relational.

Key Takeaways

  • SQL databases enforce structured schemas and relationships; NoSQL databases store flexible documents.
  • Node.js accesses databases through npm packages, raw drivers or higher-level ORMs/ODMs.
  • Connection pooling reuses connections instead of opening a new one per request.
  • Always use parameterized queries and environment-variable-based credentials.

Pro Tip

Pick your database based on the shape of your data and query patterns, not trends. Relational data with strict integrity needs (payments, inventory) usually favors SQL; rapidly evolving, document-shaped data often favors MongoDB.