This episode connects Node.js to a real database: using the pg driver with a connection pool, comparing it with the Prisma ORM, managing the connection string via environment variables, and running migrations for the database schema.

Choosing a database is only the first step. To actually use it from Node.js, you must connect the two through a driver or an ORM. A driver gives you full control over raw SQL, while an ORM provides models and type safety on top of it.
Episode 14 puts both approaches into practice with PostgreSQL: the basic connection and connection pool using the pg driver, then models with the Prisma ORM, connection string management, and migrations. You'll understand the difference and when to use each.
The pg driver is the classic choice for PostgreSQL in Node.js. Instead of creating a connection for every query, use a Pool that keeps a set of connections and lends them out when needed:
npm install pgnpm install pg adds the official PostgreSQL driver. Note: every database connection is expensive — establishing one costs time and resources. A pool solves this by reusing connections.
import pg from "pg";
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
const hasil = await pool.query("SELECT 1 AS angka");
console.log(hasil.rows);
await pool.end();new pg.Pool({ connectionString: process.env.DATABASE_URL }) creates a pool from the connection string in an environment variable. pool.query("SELECT 1 AS angka") sends SQL and returns rows. Don't forget pool.end() when the application stops, so the connections are cleaned up.
Never insert values into a query with string concatenation. Use parameter placeholders for security and clarity:
const hasil = await pool.query(
"SELECT * FROM pengguna WHERE email = $1",
[email],
);The $1 placeholder is filled from the second argument array, so input values are sent separately from the SQL and can't inject malicious commands. This is the main shield against SQL injection — we'll discuss it more deeply in episode 20.
An ORM translates a database schema into objects you can use in JavaScript. Prisma is a modern Node.js ORM with type safety and complete tooling:
npm install prisma --save-dev
npx prisma init --datasource-provider postgresqlnpx prisma init --datasource-provider postgresql creates a prisma folder with a schema.prisma file and adds DATABASE_URL to .env. The schema is the source of truth — you describe models, and Prisma generates the client and migrations.
model Pengguna {
id Int @id @default(autoincrement())
email String @unique
nama String
createdAt DateTime @default(now())
}The Pengguna model above marks id as the primary key, email as unique, and createdAt automatically filled with the current time. This declarative style replaces the manual CREATE TABLE command from episode 13 — and produces a type-safe TypeScript client.
A PostgreSQL connection string follows a fixed format:
postgresql://username:password@host:port/nama_databaseStore this value in .env as DATABASE_URL. The password part should contain encoded characters if it includes special characters. Never commit the .env file to a repository.
Schema changes are managed as migrations — a series of changes that can be applied to many databases in a definite order:
npx prisma migrate dev --name initnpx prisma migrate dev --name init creates a migration file from the schema, applies it to the development database, then generates the client. Every subsequent model change produces a new migration — this is how teams keep schemas in sync across environments.
There's no wrong answer — what matters is consistency and fit for the project's needs. Equally important: whatever you choose, the connection string stays in an environment variable and input queries are always parameterized.
Here's what to take away:
pg.Pool reuses connections, so queries are more efficient.schema.prisma and generates a client.DATABASE_URL is stored in an environment variable, not in code.npx prisma migrate dev.In the next episode, episode 15, we'll discuss models, schemas, and data validation — designing a good schema in Prisma, validating request input with Zod, and applying validation in Express routes before data touches the database. Validation is the second line of defense after safe queries.