tedious vs mssql
Connecting to Microsoft SQL Server from Node.js
tediousmssqlSimilar Packages:

Connecting to Microsoft SQL Server from Node.js

mssql and tedious are both npm packages used to interact with Microsoft SQL Server databases from Node.js applications. tedious is a low-level, pure-JavaScript implementation of the TDS (Tabular Data Stream) protocol used by SQL Server. It provides direct access to the wire protocol but requires more boilerplate for common tasks. mssql, on the other hand, is a higher-level abstraction built on top of drivers like tedious (and others), offering a cleaner, promise-based API with connection pooling, transaction support, and simplified query execution.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
tedious2,539,3391,6102.85 MB222a month agoMIT
mssql1,491,7592,267236 kB433 months agoMIT

mssql vs tedious: Choosing the Right SQL Server Client for Node.js

When your Node.js app needs to talk to Microsoft SQL Server, you’ll likely encounter two packages: mssql and tedious. While both ultimately connect to SQL Server, they serve very different roles in the stack. Let’s cut through the confusion.

🔌 Architecture: High-Level Abstraction vs Raw Protocol

mssql is a database driver abstraction layer. It doesn’t implement the SQL Server protocol itself — instead, it uses underlying drivers like tedious (for pure JavaScript) or msnodesqlv8 (for native ODBC). This lets you write clean, consistent code regardless of the backend driver.

// mssql: Simple, high-level usage
const sql = require('mssql');

await sql.connect('server=localhost;database=mydb;user=sa;password=...');
const result = await sql.query`SELECT * FROM Users WHERE id = ${userId}`;
console.log(result.recordset);

tedious is a pure-JavaScript implementation of the TDS protocol. It handles the raw byte-level communication with SQL Server but gives you no convenience methods for queries, transactions, or connection reuse.

// tedious: Low-level, verbose usage
const { Connection, Request } = require('tedious');

const config = { server: 'localhost', authentication: { ... }, options: { database: 'mydb' } };
const connection = new Connection(config);

connection.on('connect', (err) => {
  if (err) throw err;

  const request = new Request('SELECT * FROM Users WHERE id = @id', (err, rowCount) => {
    if (err) console.error(err);
  });

  request.addParameter('id', TYPES.Int, userId);
  request.on('row', (columns) => {
    console.log(columns.map(col => col.value));
  });

  connection.execSql(request);
});

connection.connect();

💡 Key insight: mssql can actually use tedious under the hood. Run npm install mssql tedious and mssql will automatically pick tedious as its driver.

🏊 Connection Management: Pooling vs Manual Control

mssql includes built-in connection pooling. You call connect() once, and it reuses connections behind the scenes. No need to manage open/closed states per query.

// mssql: Automatic pooling
const pool = new sql.ConnectionPool(config);
await pool.connect();

// Each query borrows a connection from the pool
const req1 = pool.request().query('SELECT 1');
const req2 = pool.request().query('SELECT 2');

tedious has no connection pooling. Every time you need a query, you must either reuse a single connection (risky under load) or manually implement pooling logic.

// tedious: No pooling — you’re on your own
const conn1 = new Connection(config1);
const conn2 = new Connection(config2);
// ... manage lifecycle, error recovery, concurrency limits yourself

If your app handles concurrent requests, rolling your own pool with tedious quickly becomes error-prone and complex.

📜 Query Interface: Promises vs Event Emitters

mssql uses modern async/await syntax. Queries return promises, making code linear and easy to read.

// mssql: Clean async flow
try {
  const result = await pool.request()
    .input('email', sql.VarChar, 'user@example.com')
    .query('SELECT * FROM Users WHERE email = @email');
  return result.recordset;
} catch (err) {
  console.error('Query failed:', err);
}

tedious relies on Node.js event emitters. You attach .on('row'), .on('done'), and .on('error') handlers, which leads to fragmented logic.

// tedious: Callback-heavy
const request = new Request('SELECT * FROM Users WHERE email = @email', (err) => {
  if (err) console.error(err);
});

request.addParameter('email', TYPES.VarChar, 'user@example.com');
let rows = [];

request.on('row', (columns) => {
  rows.push(columns.map(c => c.value));
});

request.on('done', () => {
  console.log('Results:', rows);
});

connection.execSql(request);

This pattern doesn’t play well with async/await unless you wrap it in a Promise yourself.

🧾 Transactions and Stored Procedures

mssql makes transactions trivial:

// mssql: Built-in transaction support
const transaction = new sql.Transaction(pool);
await transaction.begin();

try {
  await transaction.request().query('INSERT INTO Logs ...');
  await transaction.request().query('UPDATE Accounts ...');
  await transaction.commit();
} catch (err) {
  await transaction.rollback();
  throw err;
}

With tedious, you must manually execute BEGIN TRANSACTION, COMMIT, and ROLLBACK as SQL strings and track state yourself.

// tedious: Manual transaction control
const beginReq = new Request('BEGIN TRANSACTION', (err) => { /* ... */ });
connection.execSql(beginReq);
// Then run your queries...
// Then run 'COMMIT' or 'ROLLBACK' based on success/failure

Similarly, calling stored procedures in mssql is straightforward:

// mssql
await pool.request()
  .input('param', value)
  .execute('MyStoredProcedure');

In tedious, you use execProc() but still manage parameters and events manually.

⚠️ When to Avoid Each Package

  • Don’t use tedious for standard application data access. Its low-level nature introduces unnecessary complexity for 95% of use cases. Only consider it if you’re building a database proxy, custom ORM, or need to inspect TDS packets.

  • Don’t assume mssql is “slower.” The abstraction overhead is negligible compared to network latency. In fact, its built-in pooling often makes it faster in real-world apps than naive tedious usage.

✅ Shared Ground: What They Have in Common

Despite their differences, both packages:

1. Support Core SQL Server Features

  • Authentication (SQL Login, Windows Auth via Kerberos in some setups)
  • Parameterized queries (to prevent SQL injection)
  • TLS encryption
// Both support secure connections
const config = {
  server: 'prod-sql',
  options: { encrypt: true }, // enables TLS
  authentication: { type: 'default', options: { userName, password } }
};

2. Handle Data Types Correctly

  • Map JavaScript types to SQL Server types (e.g., DateDATETIME2)
  • Support large objects (VARCHAR(MAX), VARBINARY)
// mssql
.input('bio', sql.NVarChar(sql.MAX), longText)

// tedious
request.addParameter('bio', TYPES.NVarChar, longText, { length: 'MAX' });

3. Are Actively Maintained

  • Both receive regular updates for security and compatibility
  • Support modern Node.js versions (16+)

📊 Summary: Key Differences

Featuremssqltedious
Abstraction LevelHigh-level ORM-like APIRaw TDS protocol implementation
Connection Pooling✅ Built-in❌ Manual only
Async Style✅ Promises / async-await❌ Event emitters
Transactions✅ First-class support❌ Manual SQL commands
Learning CurveGentle — familiar to SQL usersSteep — requires protocol knowledge
Use CaseApplication data accessDatabase tooling / custom drivers

💡 Final Recommendation

For frontend developers integrating with SQL Server backends (or full-stack devs building Node.js APIs), use mssql. It removes boilerplate, prevents common pitfalls like connection leaks, and lets you focus on business logic.

Reserve tedious for specialized scenarios where you need to manipulate the TDS stream directly — like building a query analyzer, custom connection router, or educational tool. In day-to-day app development, it’s almost always the wrong choice.

Remember: mssql + tedious is a common combo — install both, and let mssql handle the ergonomics while tedious does the protocol work quietly underneath.

How to Choose: tedious vs mssql

  • tedious:

    Choose tedious only if you need fine-grained control over the TDS protocol, are building a database tool or proxy, or must avoid abstractions for performance or compatibility reasons. Be prepared to handle connection lifecycle, request queuing, and error recovery manually — it’s not suited for routine CRUD operations in standard apps.

  • mssql:

    Choose mssql if you want a developer-friendly, high-level interface for SQL Server with features like automatic connection pooling, promise/async-await support, and built-in transaction handling. It’s ideal for typical application development where you need to run queries without managing low-level protocol details or manual resource cleanup.

README for tedious

Tedious (node implementation of TDS)

NPM version Build Status Code Coverage

Tedious is a pure-Javascript implementation of the TDS protocol, which is used to interact with instances of Microsoft's SQL Server. It is intended to be a fairly slim implementation of the protocol, with not too much additional functionality.

NOTE: New columns are nullable by default as of version 1.11.0

Previous behavior can be restored using config.options.enableAnsiNullDefault = false. See pull request 230.

NOTE: Default login behavior has changed slightly as of version 1.2

See the changelog for version history.

Supported TDS versions

  • TDS 7.4 (SQL Server 2012/2014/2016/2017/2019/2022)
  • TDS 7.3.B (SQL Server 2008 R2)
  • TDS 7.3.A (SQL Server 2008)
  • TDS 7.2 (SQL Server 2005)
  • TDS 7.1 (SQL Server 2000)

Installation

Node.js is a prerequisite for installing tedious. Once you have installed Node.js, installing tedious is simple:

npm install tedious

Getting Started

Documentation

More documentation and code samples are available at tediousjs.github.io/tedious/

Name

Tedious is simply derived from a fast, slightly garbled, pronunciation of the letters T, D and S.

Developer Survey

We'd like to learn more about how you use tedious:

Contributing

We welcome contributions from the community. Feel free to checkout the code and submit pull requests.

License

Copyright (c) 2010-2021 Mike D Pilsbury

The MIT License

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.