Connecting to PostgreSQL with node-postgres, parameterized queries, SQL injection prevention, and a first look at Prisma ORM.
Module F-7 — Working with Databases from Node.js
What this module covers: Almost every Node.js application talks to a database. This module covers the two dominant approaches: using
node-postgres(pg) directly for full control with raw SQL, and using Prisma ORM for a type-safe, schema-driven workflow. You will learn how to connect, query, parameterize inputs to prevent SQL injection, manage connection pooling, and handle errors correctly. By the end you will have a working database layer you can drop into the Express API from F-6.
Two Approaches: Raw SQL vs ORM
Before writing code, choose your approach:
Raw SQL with node-postgres
- Full control over every query
- No abstraction layer — what you write is what runs
- Best when queries are complex, performance-critical, or team is SQL-proficient
- Requires manual migration management
Prisma ORM
- Type-safe queries with autocomplete
- Schema-first: define your models in
schema.prisma, Prisma generates the client - Auto-generated migrations
- Faster to get started, excellent for standard CRUD
- Less control over complex queries (though raw SQL escape hatch exists)
Most production codebases use one or the other, or a mix (Prisma for standard queries, raw SQL for complex reports). This module teaches both.
Option 1: node-postgres (Raw SQL)
Installing and connecting
Why a Pool, not a single connection?
A database connection is expensive to open (~5–50ms). A connection pool keeps a set of connections open and reuses them. When your code needs a connection, it borrows one from the pool, uses it, and returns it. Under high load (100 concurrent requests), each request gets a connection instantly rather than waiting to open a new one.
A connection pool is a taxi rank, not a car dealership — instead of manufacturing a brand-new car for every passenger, you keep a small fleet idling and hand off whichever one is free.
pg.Pool handles this automatically. Never use new pg.Client() for a server — that creates a single connection with no pooling.
Production story: a UPI settlement service running at roughly 20K TPS across 12 pods had pool.max: 20 on each pod — 240 connections combined, against a Postgres instance capped at max_connections = 200. A traffic spike pushed the fleet to its combined ceiling. Every pod's health check queried the database as part of its readiness probe; every one of those queries failed identically because the pool was exhausted, every pod reported unhealthy at once, and the orchestrator restarted the entire fleet simultaneously — turning a capacity spike into a full outage. The fix wasn't a bigger Postgres instance, it was arithmetic: pool.max × running instances has to stay comfortably under max_connections, with headroom for migrations, admin connections, and any other service sharing the database.
A shorter, second story from the same class of bug: a blockchain indexer stored block_number as a Postgres BIGINT. When pg returned it, blockNumber + 1 silently produced string concatenation instead of incrementing the number — see BigInt Handling below for why.
Pool Sizing at Fleet Scale
pool.max is a per-instance setting, not a global one. If you run 12 replicas of your service and each opens a pool of 20 connections, you are asking Postgres for 240 simultaneous connections, not 20. Postgres has a hard max_connections limit (default 100, commonly raised to 200–500) shared across every client connecting to it — your app fleet, migration jobs, admin consoles, read replicas, all of it.
The sizing rule of thumb:
(pool.max × number_of_running_instances) + headroom < postgres max_connections
Getting this wrong doesn't fail gracefully — it fails all at once, the way the story above played out. When scaling out horizontally (more pods/instances), scale pool.max down proportionally, or put a connection pooler like PgBouncer in front of Postgres so the fleet can grow without the connection count growing linearly with it.
Basic queries
Sign in to keep reading
The rest of this module is free — sign in with Google to unlock it and track your progress.
Sign in & RegisterDiscussion
0Join the discussion