These four packages represent different layers of the database connectivity stack for Microsoft SQL Server in Node.js. tedious and msnodesqlv8 are low-level drivers that handle the actual network protocol (TDS). mssql is a higher-level client library built on top of tedious that simplifies pooling and queries. sequelize is a full Object-Relational Mapper (ORM) that sits on top of the drivers, providing model definitions and schema management. Choosing the right one depends on how much control you need versus how much automation you want.
When building Node.js applications that rely on Microsoft SQL Server, you have four main options ranging from low-level drivers to high-level ORMs. tedious and msnodesqlv8 handle the raw network protocol. mssql wraps tedious to make querying easier. sequelize abstracts the database entirely behind JavaScript models. Let's compare how they handle real-world engineering tasks.
tedious is a low-level driver. You manage connections and send raw TDS packets or SQL strings. It gives you full control but requires more boilerplate.
// tedious: Manual connection and request
const { Connection, Request } = require('tedious');
const connection = new Connection(config);
connection.connect();
msnodesqlv8 is also a low-level driver but uses native C++ bindings. It looks similar to tedious but handles authentication differently.
// msnodesqlv8: Native driver connection
const sql = require('msnodesqlv8');
const connectionString = 'Server=.;Database=myDB;Trusted_Connection=Yes;';
sql.query(connectionString, sqlQuery, (err, results) => { /*...*/ });
mssql is a client library. It manages the connection pool for you and provides a fluent API for building requests.
// mssql: Connection pool management
const sql = require('mssql');
const pool = await sql.connect(config);
const result = await pool.request().query('SELECT * FROM Users');
sequelize is an ORM. You define models in JavaScript and rarely write raw SQL.
// sequelize: Model definition
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize(database, user, pass, { dialect: 'mssql' });
const User = sequelize.define('User', { name: DataTypes.STRING });
Setting up the initial handshake varies significantly, especially for Windows Authentication.
tedious requires a configuration object with explicit server details. It does not support Windows Auth out of the box without extra packages.
// tedious: Config object
const config = {
server: 'localhost',
authentication: {
type: 'default',
options: { userName: 'user', password: 'pass' }
}
};
msnodesqlv8 shines here. It supports Windows Integrated Security via the connection string, which is critical for corporate intranets.
// msnodesqlv8: Windows Auth string
const connStr = 'Server=localhost;Database=myDB;Trusted_Connection=Yes;';
// No username/password needed in code
mssql uses a config object similar to tedious but adds pool settings. It relies on tedious for the actual auth unless configured otherwise.
// mssql: Pool config
const config = {
user: 'user',
password: 'pass',
server: 'localhost',
pool: { max: 10, min: 0, acquireTimeoutMillis: 30000 }
};
sequelize wraps the underlying driver config. You pass the dialect options through the constructor.
// sequelize: Dialect options
const sequelize = new Sequelize('db', 'user', 'pass', {
host: 'localhost',
dialect: 'mssql',
dialectOptions: { options: { encrypt: true } }
});
How you actually get data out of the database is the biggest workflow difference.
tedious uses a request/event emitter pattern. You must handle row events manually.
// tedious: Event-based rows
const request = new Request('SELECT * FROM Users', (err) => { /*...*/ });
request.on('row', (columns) => { console.log(columns[0].value); });
connection.execSql(request);
msnodesqlv8 offers a callback-based query function or a streaming API.
// msnodesqlv8: Callback query
sql.query(connStr, 'SELECT * FROM Users', (err, results) => {
results.forEach(row => console.log(row.name));
});
mssql provides a Promise-based API that returns a clean result object with recordsets.
// mssql: Promise-based query
const result = await sql.query('SELECT * FROM Users');
console.log(result.recordset); // Array of objects
sequelize lets you query using methods that return model instances.
// sequelize: Model method
const users = await User.findAll({ where: { active: true } });
console.log(users[0].name); // Direct property access
Changing database structure over time is a common pain point. Only one of these tools solves it natively.
tedious has no schema management. You must write raw CREATE or ALTER SQL scripts and run them manually or via a separate tool.
// tedious: Raw SQL for schema
const request = new Request('CREATE TABLE Users (Id INT)', callback);
connection.execSql(request);
msnodesqlv8 also requires raw SQL for schema changes. There is no built-in migration system.
// msnodesqlv8: Raw SQL for schema
sql.query(connStr, 'ALTER TABLE Users ADD Email NVARCHAR(255)', callback);
mssql focuses on querying, not schema. You would typically pair this with a separate migration library like node-db-migrate.
// mssql: Raw SQL for schema within a transaction
await pool.request().query('CREATE INDEX idx_name ON Users (Name)');
sequelize includes a CLI for generating and running migration files. This is a major advantage for team collaboration.
// sequelize: Migration file
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.addColumn('Users', 'Email', { type: Sequelize.STRING });
}
};
The build process affects your CI/CD pipeline and deployment image size.
tedious is pure JavaScript. It installs instantly and works in serverless environments or containers without build tools.
# tedious: No compilation
npm install tedious
msnodesqlv8 requires native compilation (node-gyp). You need Python and C++ build tools installed on the server.
# msnodesqlv8: Requires build tools
npm install msnodesqlv8
# Fails if build tools are missing
mssql is pure JavaScript but depends on tedious. It installs easily like any standard npm package.
# mssql: JS only
npm install mssql
sequelize is pure JavaScript but requires a driver (like tedious) as a peer dependency. It adds more code to your bundle.
# sequelize: JS + Driver
npm install sequelize tedious
Reliability depends on how easily you can catch errors and rollback changes.
tedious handles transactions manually via SQL commands (BEGIN TRANSACTION). Errors come through the request callback.
// tedious: Manual transaction
connection.execSql(new Request('BEGIN TRANSACTION', cb));
// Must manually ROLLBACK on error
msnodesqlv8 supports transactions via SQL strings. Error handling is callback-based.
// msnodesqlv8: Transaction in SQL
sql.query(connStr, 'BEGIN TRAN; INSERT INTO...; COMMIT;', (err) => { /*...*/ });
mssql has a dedicated Transaction class that manages the state for you.
// mssql: Transaction class
const transaction = new sql.Transaction(pool);
await transaction.begin();
try { await transaction.request().query(...); await transaction.commit(); }
catch { await transaction.rollback(); }
sequelize wraps transactions in a Promise chain, passing the transaction object to queries.
// sequelize: Managed transaction
await sequelize.transaction(async (t) => {
await User.create({ name: 'John' }, { transaction: t });
});
| Feature | tedious | msnodesqlv8 | mssql | sequelize |
|---|---|---|---|---|
| Type | Driver | Native Driver | Client Library | ORM |
| Language | Pure JavaScript | C++ / Node | JavaScript | JavaScript |
| Auth | SQL Auth | Windows & SQL | SQL Auth (via tedious) | SQL Auth (via driver) |
| Query Style | Events / Raw SQL | Callback / Raw SQL | Promise / Raw SQL | Methods / Objects |
| Migrations | β Manual SQL | β Manual SQL | β Manual SQL | β Built-in CLI |
| Install | π’ Easy | π Requires Build | π’ Easy | π’ Easy |
tedious is the foundation. Use it if you are building a library yourself or need a zero-dependency (native-wise) solution for simple scripts.
msnodesqlv8 is the specialist. Use it for legacy enterprise systems requiring Windows Authentication or high-throughput binary data handling where native performance matters.
mssql is the balanced choice. Use it for most API backends where you want the power of SQL without the boilerplate of managing raw connections and pools.
sequelize is the productivity booster. Use it for complex domain models, teams that need schema versioning, or projects where database portability is a future requirement.
Final Thought: For most modern Node.js web applications, mssql offers the best balance of control and convenience. If your team prefers object-oriented design and needs migration tooling, sequelize is worth the extra abstraction cost.
Choose msnodesqlv8 if you require Windows Integrated Security (Active Directory) or need maximum performance for large binary data transfers. It is a native C++ driver, so it requires compilation tools during installation. It is best suited for enterprise environments running on Windows servers where native authentication is mandatory.
Choose mssql if you want to write raw SQL queries but need a clean API for connection pooling, transactions, and request management without the overhead of an ORM. It is built on tedious by default and offers a good balance between control and developer convenience for API backends.
Choose sequelize if you prefer working with JavaScript objects instead of SQL strings and need built-in support for migrations, validations, and associations. It is ideal for applications where database schema changes frequently or where you might switch database dialects in the future.
Choose tedious if you need a pure JavaScript implementation with no native dependencies or compilation steps. It is the reference driver for SQL Server in Node.js and is often used internally by other libraries, but you might use it directly for lightweight scripts or environments where native addons are prohibited.
Native ODBC driver for SQL Server (and Sybase ASE) for Node.js and Electron. Ships prebuilt binaries for Linux, macOS and Windows. Supports BCP, TVP, streaming, Always Encrypted, stored procedures, prepared statements, connection pooling and Windows integrated auth.
Measured end-to-end over a network (RTT ~3 ms) against SQL Server 2022 on Linux x64, Node v24, ODBC Driver 18. Schema is a 14-column trade record (bigint PK, datetime2, varchar, nvarchar, int, decimal, bit, nullable nvarchar) β a realistic OLTP row, not a narrow best-case table.
| Operation | Rows | Median | Throughput |
|---|---|---|---|
| bulk insert | 100,000 | 762 ms | 131k rows/s |
| bcp insert | 100,000 | 764 ms | 131k rows/s |
| bcp insert | 10,000 | 77 ms | 129k rows/s |
| select (array) | 100,000 | 891 ms | 112k rows/s |
| select (stream) | 100,000 | 815 ms | 122k rows/s |
Reproduce: node samples/javascript/benchmark.js --rows 1000,10000,100000 --modes bulk,bcp,select. Numbers depend on RTT, schema width and server hardware β use the script to get your own.
npm install msnodesqlv8 --save
Prebuilt binaries are downloaded automatically for Linux (x64, glibc β₯ 2.28 and musl), macOS (x64, arm64) and Windows (x64, ia32). Electron binaries are published alongside Node binaries for current major versions.
You also need a Microsoft ODBC driver on the host:
Building from source is documented in docs/building-from-source.md.
const sql = require('msnodesqlv8')
const cs = 'Driver={ODBC Driver 18 for SQL Server};Server=localhost;' +
'Database=master;UID=sa;PWD=yourStrong(!)Password;Encrypt=no'
const conn = await sql.promises.open(cs)
const res = await conn.promises.query('SELECT @@VERSION AS v')
console.log(res.first[0].v)
await conn.promises.close()
await conn.promises.query(
'INSERT INTO trades (id, symbol, qty) VALUES (?, ?, ?)',
[1, 'AAPL', 100]
)
const table = await conn.promises.getTable('trades')
await table.promises.insert(rows) // array-bind, ~130k rows/s
// table.setUseBcp(true) // opt-in to native BCP protocol
See samples/javascript/ for runnable versions of every snippet below.
| Feature | Sample | Notes |
|---|---|---|
| Connect + query | simple-demo.js | callback and promise APIs |
| Streaming results | streaming.js | on('row'), on('column'), pause/resume |
| Stored procedures | procedure.js | named params, output params, return code |
| Table-valued parameters | tvp.js | build TVP from object array |
| Bulk insert / update | table-builder.js | BulkTableOpMgr array bind |
| BCP fast insert | benchmark.js | table.setUseBcp(true) β ODBC 17/18 only |
| Connection pool | simple-pool.js | built-in, no external dep |
| Pool scaling strategies | pool-scaling.js | see docs/pool-efficient-strategy.md |
| Prepared statements | test/prepared.test.js | reuse parsed plan across calls |
| Transactions | txn.js | explicit begin/commit/rollback |
| Pause / resume long query | paged-procedure-pause-resume.js | backpressure for large result sets |
| Thread workers | thread-workers.js | offload queries to worker_threads |
| Benchmark harness | benchmark.js | reproduces the numbers above |
Full API reference lives in the wiki.
Full runnable projects in their own repos, showing msnodesqlv8 wired into real frameworks. The driver is a native addon β do not call it from a UI thread (renderer process, Next.js client components). Use a server route, API handler or worker.
| Stack | Repo |
|---|---|
| Next.js (pages router) | todo-with-nextjs_msnodesqlv8 |
| Next.js (app router) | todo-with-nextjs-app-router_msnodesqlv8 |
| Vite + Express | msnodesqlv8-vite |
| TypeScript | msnodesqlv8_ts_sample |
| JavaScript with IDE typings | msnodesqlv8_yarn_sample |
| Sequelize | msnodesqlv8-sequelize |
mssql package over this driver | msnodesqlv8_mssql_sample |
| Electron | msnodesqlv8-electron |
| React | msnodesqlv8-react |
| Platform | Arch | Node | Electron |
|---|---|---|---|
| Linux (glibc β₯ 2.28) | x64 | 20, 22, 24 | 32+ |
| Linux (musl / Alpine) | x64 | 20, 22, 24 | 32+ |
| macOS | x64, arm64 | 20, 22, 24 | 32+ |
| Windows | x64, ia32 | 20, 22, 24 | 32+ |
| Windows Integrated Auth | x64 | supported via Trusted_Connection=yes | β |
Tested against SQL Server 2017, 2019, 2022. Sybase ASE support is smaller in scope β see samples/javascript/sybase-query.js and the wiki.
IM002: Data source name not found β no matching ODBC driver installed. On Linux/macOS check odbcinst -q -d. On Windows check ODBC Data Sources (64-bit).
SSL Provider: certificate verify failed on newer SQL Server β add Encrypt=yes;TrustServerCertificate=yes to the connection string, or install the server certificate.
Segfault on Ubuntu/Debian with Node 18/20 β requires OpenSSL 3.2. See tool/openssl.sh in this repo and the wiki install notes.
BCP crashes or silently falls back β BCP requires ODBC Driver 17 or 18 exactly. Any older driver (SQL Server Native Client, FreeTDS) will either crash the process or silently no-op. Check with odbcinst -q -d.
Prebuilt binary fails to load β your glibc, Node ABI or Electron version may not match a published binary. Try building from source: docs/building-from-source.md.
More issues and workarounds: GitHub Issues.
Apache 2.0. See LICENSE.txt.