Roadmap › Node.js — Runtime Internals, Express, APIs and SQL Server › Module: Building APIs
Lessons in this course (11)
8. Connecting Node.js to SQL Server (mssql, Prisma, Sequelize)
Intermediate★ Must knowLesson 8 of 11
1. mssql package (most common, uses the tedious driver):
js
// src/db/pool.js
import sql from 'mssql';
const config = {
server: process.env.DB_SERVER, // e.g. "localhost" or "myserver.database.windows.net"
database: process.env.DB_NAME,
user: process.env.DB_USER, // SQL authentication
password: process.env.DB_PASSWORD,
port: 1433,
pool: { max: 10, min: 0, idleTimeoutMillis: 30000 },
options: {
encrypt: true, // required for Azure SQL
trustServerCertificate: process.env.NODE_ENV !== 'production', // local dev with self-signed cert
},
};
export const poolPromise = new sql.ConnectionPool(config).connect(); // ONE pool for the whole app
export { sql };
// src/modules/products/product.repository.js
import { poolPromise, sql } from '../../db/pool.js';
export async function findById(id) {
const pool = await poolPromise;
const result = await pool.request()
.input('id', sql.Int, id) // parameterised — prevents SQL injection
.query('SELECT Id, Name, Price FROM dbo.Products WHERE Id = @id');
return result.recordset[0] ?? null;
}
export async function searchByProc(customerId) {
const pool = await poolPromise;
const r = await pool.request()
.input('CustomerId', sql.Int, customerId)
.output('Total', sql.Int)
.execute('dbo.usp_GetCustomerOrders'); // stored procedure
return { orders: r.recordset, total: r.output.Total };
}
export async function placeOrder(order) { // transaction
const pool = await poolPromise;
const tx = new sql.Transaction(pool);
await tx.begin();
try {
const r = await new sql.Request(tx).input('cid', sql.Int, order.customerId)
.query('INSERT INTO dbo.Orders (CustomerId) OUTPUT INSERTED.Id VALUES (@cid)');
for (const l of order.lines)
await new sql.Request(tx).input('oid', sql.Int, r.recordset[0].Id).input('pid', sql.Int, l.productId).input('qty', sql.Int, l.qty)
.query('INSERT INTO dbo.OrderLines (OrderId, ProductId, Qty) VALUES (@oid, @pid, @qty)');
await tx.commit();
} catch (e) { await tx.rollback(); throw e; }
}bash
# .env (never commit)
DB_SERVER=localhost
DB_NAME=Shop
DB_USER=shop_app
DB_PASSWORD=change-meWindows authentication from Node needs the msnodesqlv8 driver (mssql/msnodesqlv8, Windows only); for Azure SQL prefer Microsoft Entra ID auth (authentication: { type: 'azure-active-directory-default' }) so no password is stored.
2. Prisma ORM:
prisma
// prisma/schema.prisma
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Product {
id Int @id @default(autoincrement())
name String @db.NVarChar(200)
price Decimal @db.Decimal(18, 2)
}bash
DATABASE_URL="sqlserver://localhost:1433;database=Shop;user=shop_app;password=change-me;encrypt=true;trustServerCertificate=true"
npx prisma migrate dev --name init # or: npx prisma db pull (database-first)js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const cheap = await prisma.product.findMany({ where: { price: { lt: 100 } }, orderBy: { name: 'asc' } });3. Sequelize: new Sequelize(db, user, pass, { dialect: 'mssql', host, dialectOptions: { options: { encrypt: true } } }), define models, findAll({ where }).