pg vs mssql vs sqlite3 vs mysql
Choosing the Right SQL Driver for Node.js Backends
pgmssqlsqlite3mysqlSimilar Packages:

Choosing the Right SQL Driver for Node.js Backends

mssql, mysql, pg, and sqlite3 are Node.js drivers that allow applications to communicate with specific SQL database engines. pg connects to PostgreSQL, mysql connects to MySQL/MariaDB, mssql connects to Microsoft SQL Server, and sqlite3 connects to local SQLite files. Each package handles connection pooling, query execution, and data type mapping differently, impacting performance and deployment strategies in server-side JavaScript environments.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
pg33,550,39513,17695.2 kB51014 days agoMIT
mssql2,571,3392,275267 kB239 days agoMIT
sqlite32,305,5826,4163.4 MB1744 months agoBSD-3-Clause
mysql1,268,88018,620-1756 years agoMIT

Choosing the Right SQL Driver for Node.js Backends

When building server-side JavaScript applications, picking the right database driver is a critical architectural decision. The packages mssql, mysql, pg, and sqlite3 each connect to different database engines, and they handle connections, queries, and data types in unique ways. This guide breaks down the technical differences to help you choose the right tool for your stack.

🗄️ Database Engine Compatibility

The most obvious difference is which database system each package talks to. You cannot swap these packages without changing your underlying database infrastructure.

pg connects exclusively to PostgreSQL. It is the go-to choice for modern web apps that need complex queries, JSON support, and strong data integrity.

// pg: Connects to PostgreSQL
const { Pool } = require('pg');
const pool = new Pool({ connectionString: 'postgres://user:pass@localhost:5432/db' });

mysql connects to MySQL and MariaDB. It is widely used in legacy LAMP stacks but lacks some advanced features found in Postgres.

// mysql: Connects to MySQL
const mysql = require('mysql');
const connection = mysql.createConnection({ host: 'localhost', user: 'user', password: 'pass', database: 'db' });

mssql connects to Microsoft SQL Server and Azure SQL. It is essential for enterprise environments running Windows-based infrastructure.

// mssql: Connects to MS SQL Server
const sql = require('mssql');
const config = { user: 'user', password: 'pass', server: 'localhost', database: 'db' };

sqlite3 connects to SQLite files. It does not require a separate server process, making it portable but limited in concurrency.

// sqlite3: Connects to a local file
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./database.sqlite');

🔌 Connection Management: Pools vs Single Connections

How a driver manages connections determines how well your app handles multiple users at once.

pg uses a connection pool by default. This keeps a set of open connections ready to use, which is much faster than opening a new one for every request.

// pg: Built-in connection pool
const { Pool } = require('pg');
const pool = new Pool({ max: 20 });
const client = await pool.connect();
await client.query('SELECT NOW()');
client.release();

mysql creates a single connection or a cluster. You often need to manually manage pooling or use a wrapper to get performance similar to pg.

// mysql: Manual connection handling
const mysql = require('mysql');
const connection = mysql.createConnection({ /* config */ });
connection.connect();
connection.query('SELECT NOW()', (err, results) => { /* handle */ });

mssql relies on an internal pool managed by the underlying tedious driver. You configure the pool size in the connection config object.

// mssql: Configured pool in connection request
const sql = require('mssql');
const pool = await sql.connect({ pool: { max: 10 }, /* config */ });
const result = await pool.request().query('SELECT GETDATE()');

sqlite3 opens a direct handle to the file. It does not support connection pooling in the same way because the database is a single file locked during writes.

// sqlite3: Direct file handle
const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('./db.sqlite');
db.serialize(() => db.run('CREATE TABLE IF NOT EXISTS logs (msg TEXT)'));

📝 Query Execution Styles

Each package has a different way of sending SQL commands to the database. Some use callbacks, some use Promises, and some use query builders.

pg supports both callbacks and Promises (via async/await). It separates the query text from the values array to prevent SQL injection.

// pg: Promise-based query with parameters
const res = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
console.log(res.rows);

mysql traditionally uses callbacks. While it supports Promises in newer versions or via mysql2, the core mysql package leans heavily on callback patterns.

// mysql: Callback-based query
connection.query('SELECT * FROM users WHERE id = ?', [userId], (err, results) => {
  if (err) throw err;
  console.log(results);
});

mssql uses a Request object to build queries. This adds a layer of abstraction that helps with prepared statements and stored procedures.

// mssql: Request object pattern
const request = new sql.Request();
request.input('id', sql.Int, userId);
const result = await request.query('SELECT * FROM users WHERE id = @id');

sqlite3 uses methods like run, get, and all. These are asynchronous but often rely on callbacks, which can lead to nested code if not wrapped in Promises.

// sqlite3: Method-based execution
db.get('SELECT * FROM users WHERE id = ?', [userId], (err, row) => {
  if (err) throw err;
  console.log(row);
});

⚠️ Deprecation and Maintenance Status

One of these packages has a significant warning attached to it that affects new projects.

mysql is effectively in maintenance mode. The community and maintainers recommend using mysql2 for new projects because it offers better performance, Promise support, and newer protocol features. Using the original mysql package in a new architecture is risky.

// mysql: Legacy package (Use mysql2 instead)
// npm install mysql2
const mysql = require('mysql2/promise'); // Recommended modern approach

pg is actively maintained and is the standard for Postgres in Node.js. It receives regular updates for security and performance.

// pg: Actively maintained
// npm install pg
const { Pool } = require('pg');

mssql is actively maintained and tracks updates from the underlying TDS protocol. It is stable for enterprise use cases.

// mssql: Actively maintained
// npm install mssql
const sql = require('mssql');

sqlite3 is maintained but relies on native C++ bindings. This often causes installation issues on different operating systems or Node versions, requiring rebuilds.

// sqlite3: Native bindings can cause install issues
// npm install sqlite3
// Often requires node-gyp to compile locally

🔒 Security and SQL Injection Prevention

Preventing SQL injection is non-negotiable. All four packages support parameterized queries, but the syntax differs.

pg uses dollar signs ($1, $2) for placeholders. This is clear and strictly enforced.

// pg: Parameterized query
await pool.query('SELECT * FROM users WHERE email = $1', [email]);

mysql uses question marks (?) for placeholders. You must ensure the array order matches the query.

// mysql: Parameterized query
connection.query('SELECT * FROM users WHERE email = ?', [email], callback);

mssql uses named parameters (@name). This makes complex queries easier to read.

// mssql: Named parameters
request.input('email', sql.VarChar, email);
await request.query('SELECT * FROM users WHERE email = @email');

sqlite3 also uses question marks (?) similar to MySQL. It is simple but requires careful array management.

// sqlite3: Parameterized query
db.get('SELECT * FROM users WHERE email = ?', [email], callback);

🚀 Performance and Concurrency

Performance depends heavily on the database engine, but the driver overhead matters too.

pg is highly optimized for concurrent connections. It handles thousands of simultaneous requests well when paired with a connection pooler like PgBouncer.

// pg: High concurrency support
const pool = new Pool({ max: 50, idleTimeoutMillis: 30000 });

mysql performs well but the original driver is single-threaded in its JS execution. mysql2 improves this significantly.

// mysql: Standard performance
// Limited by callback overhead in high-load scenarios

mssql has higher overhead due to the TDS protocol complexity. It is robust but generally slower than pg for simple CRUD operations.

// mssql: Enterprise grade overhead
// Optimized for complex transactions rather than raw speed

sqlite3 is fast for reads but locks the whole file for writes. This makes it unsuitable for high-traffic web apps where many users write data simultaneously.

// sqlite3: Write locking limitation
// Only one write transaction can happen at a time

📊 Summary: Key Differences

Featurepgmysqlmssqlsqlite3
DatabasePostgreSQLMySQL/MariaDBMS SQL ServerSQLite File
ConnectionBuilt-in PoolManual/ClusterInternal PoolDirect File Handle
Query StylePromises/CallbacksCallbacksRequest ObjectMethods (run/get)
Parameters$1, $2?@name?
Best ForModern Web AppsLegacy SystemsEnterprise/WindowsLocal/Embedded
WarningNoneUse mysql2Config HeavyWrite Locking

💡 The Big Picture

pg is the default choice for new Node.js projects. PostgreSQL is powerful, and the pg driver is stable, well-documented, and handles concurrency beautifully.

mysql should be avoided for new work. If you must use MySQL, reach for mysql2 instead. The original package is outdated.

mssql is a specialized tool. Only use it if your company requires SQL Server. It works well but adds complexity to your deployment.

sqlite3 is great for testing and small tools. Do not use it for a production web server that expects heavy traffic.

Final Thought: The driver you pick locks you into a database ecosystem. Choose pg for flexibility and growth, mssql for enterprise compliance, and sqlite3 for simplicity. Avoid mysql in favor of its modern successor.

How to Choose: pg vs mssql vs sqlite3 vs mysql

  • pg:

    Choose pg if you are using PostgreSQL, which is the most popular open-source relational database for new web applications. It offers robust connection pooling, strong type support, and a massive ecosystem of extensions. It is the safest bet for scalability and community support.

  • mssql:

    Choose mssql if your infrastructure relies on Microsoft SQL Server or Azure SQL Database. It is the standard choice for enterprise environments already invested in the Microsoft ecosystem. Be aware that it requires specific network configurations and often runs heavier than open-source alternatives.

  • sqlite3:

    Choose sqlite3 for local development, testing, or small-scale embedded applications where a separate database server is overkill. Avoid it for high-concurrency production web servers because it locks the entire database file during writes, which creates bottlenecks.

  • mysql:

    Choose mysql only for maintaining legacy systems. For new projects, mysql2 is the recommended alternative due to better performance and Promise support. The original mysql package lacks modern features and is slower, making it a risky choice for greenfield development.

README for pg

node-postgres

Build Status NPM version NPM downloads

Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.

Install

$ npm install pg

:star: Documentation :star:

Features

  • Fastest PostgreSQL client for Node.js
  • Pure JavaScript client and native libpq bindings share the same API
  • Connection pooling
  • Extensible JS ↔ PostgreSQL data-type coercion
  • Supported PostgreSQL features
    • Parameterized queries
    • Named statements with query plan caching
    • Async notifications with LISTEN/NOTIFY
    • Bulk import & export with COPY TO/COPY FROM

Extras

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

Support

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:

  • version of Node
  • version of Postgres
  • smallest possible snippet of code to reproduce the problem

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.

Sponsorship :two_hearts:

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.

Featured sponsor

Special thanks to medplum for their generous and thoughtful support of node-postgres!

medplum

Contributing

:heart: contributions!

I will happily accept your pull request if it:

  • has tests
  • looks reasonable
  • does not break backwards compatibility

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.

Troubleshooting and FAQ

The causes and solutions to common errors can be found among the Frequently Asked Questions (FAQ)

License

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.