sql-server - puede - sql server express
¿Cómo puedo usar un único grupo de conexiones mssql en varias rutas en una aplicación web Express 4? (6)
Quiero usar node-mssql como un conector de base de datos MSSQL en una aplicación web Node JS Express 4. La lógica del controlador de ruta se maneja en archivos separados.
¿Cómo creo un grupo de conexiones único / global y lo uso en varios archivos donde se maneja la lógica de ruta? No quiero hacer un nuevo grupo de conexiones en cada función / archivo de controlador de ruta.
src/config.js
export default {
database: {
server: process.env.DATABASE_SERVER || ''<server>.database.windows.net'',
port: 1433,
user: process.env.DATABASE_USER || ''<user>@<server>'',
password: process.env.DATABASE_PASSWORD || ''<password>'',
database: process.env.DATABASE_NAME || ''<database>'',
connectionTimeout: 30000,
driver: ''tedious'',
stream: false,
options: {
appName: ''<app-name>'',
encrypt: true
}
}
};
src/server.js
import sql from ''mssql'';
import express from ''express'';
import config from ''./config'';
// Create and configure an HTTP server
const server = express();
server.set(''port'', (process.env.PORT || 5000));
// Register Express routes / middleware
server.use(''/api/user'', require(''./api/user'');
// Open a SQL Database connection and put it into the global
// connection pool, then launch the HTTP server
sql.connect(config.database, err => {
if (err) {
console.log(''Failed to open a SQL Database connection.'', err.stack);
}
server.listen(server.get(''port''), () => {
console.log(''Node app is running at http://127.0.0.1:'' + server.get(''port''));
});
});
sql.on(''error'', err => console.log(err.stack));
src/api/user.js
import sql from ''mssql'';
import { Router } from ''express'';
const router = new Router();
router.get(''/:id'', async (req, res, next) => {
try {
const request = new sql.Request();
request.input(''UserID'', req.params.id);
request.multiple = true;
const dataset = await request.query(`
SELECT UserID, Name, Email
FROM [User] WHERE UserID = @UserID;
SELECT r.RoleName FROM UserRole AS r
INNER JOIN [User] AS u ON u.UserID = r.UserID
WHERE u.UserID = @UserID
`);
const user = dataset[0].map(row => ({
id: row.UserID,
name: row.Name,
email: row.Email,
roles: dataset[1].map(role => role.RoleName)
})).shift();
if (user) {
res.send(user);
} else {
res.statusCode(404);
}
} catch (err) {
next(err);
}
});
export default router;
Consulte también MSSQL SDK para Node.js , T-SQL Reference , React Starter Kit
Así es como lo hice, lo que creo que es un poco más simple que algunas de las otras soluciones.
Archivo de base de datos (db.js):
const sql = require(''mssql'')
const config = {}
const pool = new sql.ConnectionPool(config)
.connect()
.then(pool => {
console.log(''Connected to MSSQL'')
return pool
})
.catch(err => console.log(''Database Connection Failed! Bad Config: '', err))
module.exports = {
sql, pool
}
Consulta:
const { pool, sql } = require(''../db'')
return pool.then(conn => {
const ps = new sql.PreparedStatement(conn)
ps.input(''xxxx'', sql.VarChar)
return ps.prepare(`SELECT * from table where xxxx = @xxxx`)
.then(data => ps.execute({ xxxx: ''xxxx'' }))
})
EDITAR: actualizado para que coincida con la esencia de Christiaan Westerbeek, que era mucho más limpia.
Cuando configure su aplicación (como cuando crea el servidor express), establezca la conexión DB. ¡Asegúrese de hacer esto ANTES de que necesite todas sus rutas! (Finagle los requisitos en la parte superior del archivo)
Al igual que los documentos:
var sql = require(''mssql''); var connection = new sql.Connection(..... //store the connection sql.globalConnection = connection;
Luego, en todos sus archivos de ruta, puede hacer esto:
var sql = require(''mssql''); var sqlConn = sql.globalConnection; var request = new sql.Request(sqlConn); //...
¡Deberias hacer eso!
Dicho todo esto, use knex para administrar su construcción de consultas MySQL. Tiene un grupo de conexiones incorporado y almacena la instancia de knex conectada de la misma manera. Así como una generosa ración de genial.
Han pasado 3 años desde que pregunté y respondí la pregunta. Desde entonces, algunas cosas han cambiado. Aquí está la nueva solución basada en ES6, mssql 4 y Express 4 que sugeriría hoy.
Dos elementos clave están en juego aquí.
- Los módulos se almacenan en caché después de la primera vez que se cargan. Esto significa que cada llamada a requerir (''./ db'') devolverá exactamente el mismo objeto. El primer requerimiento de db.js ejecutará ese archivo y creará la promesa y lo exportará. El segundo requerimiento de db.js devolverá ESA misma promesa sin ejecutar el archivo. Y es esa promesa la que se resolverá con el grupo.
- Una promesa puede ser luego justificada nuevamente. Y si se resolvió antes, se resolverá inmediatamente de nuevo con lo que resolvió la primera vez, que es el grupo.
En
server.js
const express = require(''express'')
// require route handlers.
// they will all include the same connection pool
const set1Router = require(''./routes/set1'')
const set2Router = require(''./routes/set2'')
// generic express stuff
const app = express()
// ...
app.use(''/set1'', set1Router)
app.use(''/set2'', set2Router)
// No need to connect the pool
// Just start the web server
const server = app.listen(process.env.PORT || 3000, () => {
const host = server.address().address
const port = server.address().port
console.log(`Example app listening at http://${host}:${port}`)
})
En
db.js
const sql = require(''mssql'')
const config = {/*...*/}
const poolPromise = new sql.ConnectionPool(config)
.connect()
.then(pool => {
console.log(''Connected to MSSQL'')
return pool
})
.catch(err => console.log(''Database Connection Failed! Bad Config: '', err))
module.exports = {
sql, poolPromise
}
En
routes/set1.js
y
routes/set2.js
const express = require(''express'')
const router = express.Router()
const { poolPromise } = require(''./db'')
router.get(''/'', async (req, res) => {
try {
const pool = await poolPromise
const result = await pool.request()
.input(''input_parameter'', sql.Int, req.query.input_parameter)
.query(''select * from mytable where id = @input_parameter'')
res.json(result.recordset)
} catch (err) {
res.status(500)
res.send(err.message)
}
})
module.exports = router
Para resumir
Siempre obtendrá la misma promesa debido al almacenamiento en caché del módulo y esa promesa, una y otra vez, se resolverá con el grupo que resolvió por primera vez. Por lo tanto, cada archivo de enrutador utiliza el mismo grupo.
Por CIERTO: hay formas más fáciles de realizar el intento de captura en la ruta rápida que no cubriré en esta respuesta. Lea sobre esto aquí: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016
La vieja solución
Esta es la solución que publiqué hace 3 años, porque creía que tenía una respuesta que valía la pena compartir y no pude encontrar una solución documentada en otro lugar. También en algunos temas ( #118 , #164 , #165 ) en node-mssql se discute este tema.
En
server.js
var express = require(''express'');
var sql = require(''mssql'');
var config = {/*...*/};
//instantiate a connection pool
var cp = new sql.Connection(config); //cp = connection pool
//require route handlers and use the same connection pool everywhere
var set1 = require(''./routes/set1'')(cp);
var set2 = require(''./routes/set2'')(cp);
//generic express stuff
var app = express();
//...
app.get(''/path1'', set1.get);
app.get(''/path2'', set2.get);
//connect the pool and start the web server when done
cp.connect().then(function() {
console.log(''Connection pool open for duty'');
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log(''Example app listening at http://%s:%s'', host, port);
});
}).catch(function(err) {
console.error(''Error creating connection pool'', err);
});
En
routes/set1.js
var sql = require(''mssql'');
module.exports = function(cp) {
var me = {
get: function(req, res, next) {
var request = new sql.Request(cp);
request.query(''select * from test'', function(err, recordset) {
if (err) {
console.error(err);
res.status(500).send(err.message);
return;
}
res.status(200).json(recordset);
});
}
};
return me;
};
No estoy loco por los ejemplos que he visto hasta ahora para configurar una conexión agrupada. Hago:
const pool = new mssql.ConnectionPool(msConfig).connect()
.then(_ => { return _ } )
.catch(e => console.error("Database Trouble! ", e))
/* ... */
pool
.then(_ => _.query( /* ... */ )
.then(result => { /* ... */ })
.catch(e => { /* ... */ })
Utilicé un concepto similar (
single connection pool
), pero envolví la lógica de conexión en un archivo (no es necesario pasar el grupo de conexión a otros lugares).
connPoolPromise
continuación solo se inicializará una vez ya que los módulos se almacenan en caché después de la primera vez que se cargan.
por ejemplo
DBUtil.js
const sql = require(''mssql'');
const dbConfig = require(''./dbconfig'');
let connPoolPromise = null;
const getConnPoolPromise = () => {
if (connPoolPromise) return connPoolPromise;
connPoolPromise = new Promise((resolve, reject) => {
const conn = new sql.ConnectionPool(dbConfig);
conn.on(''close'', () => {
connPoolPromise = null;
});
conn.connect().then(connPool => {
return resolve(connPool);
}).catch(err => {
connPoolPromise = null;
return reject(err);
});
});
return connPoolPromise;
}
// Fetch data example using callback
exports.query = (sqlQuery, callback) => {
getConnPoolPromise().then(connPool => {
return connPool.request().query(sqlQuery);
}).then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
};
Uso
user.js
:
const DBUtil = require(''./DBUtil'');
DBUtil.query(''select * from user where userId = 12'', (err, recordsets) => {
if (err) return callback(err);
// Handle recordsets logic
}