pg, pg-promise, postgresql, and sequelize are all tools used to connect Node.js applications to PostgreSQL databases, but they operate at different levels of abstraction. pg is the standard, low-level client library that provides direct access to database features. pg-promise builds on top of pg to offer a promise-based interface with additional convenience methods for handling transactions and batches. postgresql is often a wrapper or alternative client that aims to simplify connection strings but lacks the ecosystem support of pg. sequelize is a full Object-Relational Mapper (ORM) that abstracts SQL entirely, allowing developers to interact with the database using JavaScript objects and models instead of raw queries.
When building backend services or full-stack applications with Node.js, connecting to a PostgreSQL database is a common requirement. The ecosystem offers several options ranging from low-level drivers to high-level ORMs. pg is the foundational client, pg-promise enhances it with promises, postgresql serves as an alternative wrapper, and sequelize abstracts the database entirely. Let's compare how they handle real-world engineering tasks.
pg is the raw driver.
// pg: Direct client usage
const { Pool } = require('pg');
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = await pool.connect();
pg-promise is a promise-based layer on top of pg.
// pg-promise: Database instance
const pgp = require('pg-promise')();
const db = pgp(process.env.DATABASE_URL);
// db is ready to query immediately
postgresql is often a thin wrapper.
pg.// postgresql: Simplified connection
const postgresql = require('postgresql');
const client = new postgresql.Client(process.env.DATABASE_URL);
await client.connect();
sequelize is a full ORM.
// sequelize: Model definition
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL);
const User = sequelize.define('User', { name: Sequelize.STRING });
pg requires you to write SQL strings explicitly.
query method.// pg: Raw query
const res = await client.query('SELECT $1::text as message', ['Hello']);
console.log(res.rows[0].message);
pg-promise also uses SQL strings but simplifies the result.
// pg-promise: Query with named parameters
const user = await db.one('SELECT * FROM users WHERE id = $1', userId);
console.log(user.name);
postgresql mimics the standard client API.
query methods as pg.// postgresql: Query execution
const res = await client.query('SELECT NOW()');
console.log(res.rows[0]);
sequelize avoids SQL strings for common operations.
findAll or create.// sequelize: Model method
const users = await User.findAll({ where: { active: true } });
// Or raw query
const [results] = await sequelize.query('SELECT * FROM users');
pg requires manual transaction control.
BEGIN, COMMIT, and ROLLBACK commands.// pg: Manual transaction
await client.query('BEGIN');
try {
await client.query('INSERT INTO...');
await client.query('UPDATE...');
await client.query('COMMIT');
} catch (e) {
await client.query('ROLLBACK');
throw e;
}
pg-promise provides a dedicated transaction API.
db.tx.// pg-promise: Automatic transaction
await db.tx(async (t) => {
await t.none('INSERT INTO...');
await t.none('UPDATE...');
});
// Rolls back automatically if an error is thrown
postgresql typically follows the manual approach.
pg, you manage the transaction state.pg-promise.// postgresql: Manual transaction control
await client.query('BEGIN');
// ... execute queries
await client.query('COMMIT');
sequelize wraps transactions in a context object.
// sequelize: Transaction context
await sequelize.transaction(async (t) => {
await User.create({ name: 'Alice' }, { transaction: t });
await Post.create({ title: 'Hi' }, { transaction: t });
});
pg throws standard JavaScript errors.
try/catch blocks.// pg: Error handling
try {
await client.query('SELECT * FROM users WHERE id = $1', [id]);
} catch (err) {
console.error('Query failed', err);
}
pg-promise offers configurable error handling.
// pg-promise: Global error handling
const initOptions = {
error: (err, e) => {
console.log('Global DB error:', err.message);
}
};
const pgp = require('pg-promise')(initOptions);
postgresql relies on standard promise rejection.
// postgresql: Catching errors
client.query('SELECT * FROM...').catch(err => {
console.error(err);
});
sequelize has built-in validation.
allowNull: false).// sequelize: Model validation
const User = sequelize.define('User', {
email: { type: Sequelize.STRING, validate: { isEmail: true } }
});
// Throws error if email is invalid before querying
While the differences are clear, all these packages share the goal of connecting Node.js to PostgreSQL securely.
// pg: Pool configuration
const pool = new Pool({ max: 20 });
// sequelize: Pool configuration
const sequelize = new Sequelize(url, { pool: { max: 20 } });
// pg-promise: Safe parameters
db.one('SELECT * FROM users WHERE id = $1', [userId]);
// sequelize: Safe replacements
sequelize.query('SELECT * FROM users WHERE id = $1', { bind: [userId] });
// All packages support this pattern
async function getData() {
const result = await db.query('SELECT 1');
return result;
}
| Feature | pg | pg-promise | postgresql | sequelize |
|---|---|---|---|---|
| Type | π Low-level Driver | π Enhanced Driver | π Wrapper/Alternative | π§± Full ORM |
| Query Style | π Raw SQL | π Raw SQL + Helpers | π Raw SQL | π§© Model Methods |
| Transactions | π οΈ Manual | β Automatic | π οΈ Manual | β Automatic |
| Learning Curve | π Medium | π Medium | π Medium | π Low (for JS devs) |
| Performance | β‘ High | β‘ High | β‘ High | β‘ Medium (Abstraction overhead) |
| Maintenance | π’ Active | π’ Active | π‘ Less Active | π’ Active |
pg is the foundation ποΈ. It is the standard choice for teams who want control, performance, and direct SQL access without extra abstraction. It is the safest bet for long-term maintenance.
pg-promise is the productivity booster π. It keeps the power of pg but removes boilerplate for transactions and error handling. Ideal for teams writing raw SQL who want cleaner code.
postgresql is the alternative π. It exists but lacks the ecosystem dominance of pg. Use it only if you have a specific reason, otherwise stick to pg for better community support.
sequelize is the abstraction layer π§±. It speeds up development by removing SQL from the daily workflow. Best for CRUD-heavy apps where query optimization is secondary to development speed.
Final Thought: For most professional Node.js backends, pg or pg-promise offers the best balance of control and reliability. Choose sequelize only if your team strongly prefers ORM patterns over writing SQL.
Choose pg if you need full control over SQL queries and want the industry-standard driver with the widest community support. It is ideal for projects where performance matters and you prefer writing raw SQL over using an abstraction layer.
Choose pg-promise if you want the reliability of pg but prefer a cleaner promise-based API with built-in helpers for transactions and batch operations. It suits teams that want to reduce boilerplate without losing the ability to write raw SQL.
Choose postgresql only if you have a specific legacy requirement or need a thin wrapper that simplifies connection strings, but be aware it has less maintenance and community support than pg. For most new projects, pg is the safer and more standard choice.
Choose sequelize if you want to avoid writing SQL entirely and prefer defining database schemas as JavaScript models. It is best for applications where developer speed and abstraction are more important than fine-grained query optimization.
Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.
$ npm install pg
LISTEN/NOTIFYCOPY TO/COPY FROMnode-postgres is by design pretty light on abstractions. These are some handy modules we've been using over the years to complete the picture. The entire list can be found on our wiki.
node-postgres is free software. If you encounter a bug with the library please open an issue on the GitHub repo. If you have questions unanswered by the documentation please open an issue pointing out how the documentation was unclear & I will do my best to make it better!
When you open an issue please provide:
You can also follow me @brianc on bluesky if that's your thing for updates on node-postgres with nearly zero non node-postgres content. My old twitter/x account is no longer used.
node-postgres's continued development has been made possible in part by generous financial support from the community.
If you or your company are benefiting from node-postgres and would like to help keep the project financially sustainable please consider supporting its development.
Special thanks to medplum for their generous and thoughtful support of node-postgres!

:heart: contributions!
I will happily accept your pull request if it:
If your change involves breaking backwards compatibility please please point that out in the pull request & we can discuss & plan when and how to release it and what type of documentation or communicate it will require.
The causes and solutions to common errors can be found among the Frequently Asked Questions (FAQ)
Copyright (c) 2010-2020 Brian Carlson (brian.m.carlson@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.