web-dev-qa-db-fra.com

Comment puis-je utiliser un seul pool de connexions mssql sur plusieurs itinéraires dans une application Web Express 4?

Je veux utiliser node-mssql comme connecteur de base de données MSSQL dans une application Web Node JS Express 4. La logique du gestionnaire d'itinéraire est gérée dans des fichiers séparés.

Comment créer un pool de connexions unique/global et l'utiliser sur plusieurs fichiers où la logique de routage est gérée? Je ne veux pas créer un nouveau pool de connexions dans chaque fonction/fichier de gestionnaire d'itinéraire.

25

Cela fait 3 ans que j'ai demandé et répondu à la question. Depuis lors, certaines choses ont changé. Voici la nouvelle solution basée sur ES6, mssql 4 et Express 4 que je proposerais aujourd'hui.

Deux éléments clés sont en jeu ici.

  1. Les modules sont mis en cache après la première fois qu'ils sont chargés. Cela signifie que chaque appel à require ('./ db') retournera exactement le même objet. Le premier besoin de db.js exécutera ce fichier et créera la promesse et l'exportera. Le second besoin de db.js renverra CETTE promesse sans exécuter le fichier. Et c'est cette promesse qui se résoudra avec la piscine.
  2. Une promesse peut être à nouveau concrétisée. Et s'il a résolu avant, il résoudra immédiatement à nouveau avec ce qu'il a résolu la première fois, qui est la piscine.

Dans 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}`)
})

Dans 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
}

Dans routes/set1.js et 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

Pour résumer

Vous obtiendrez toujours la même promesse en raison de la mise en cache du module et cette promesse sera, encore et encore, résolue avec le pool qu'elle a résolu la première fois. Ainsi, chaque fichier routeur utilise le même pool.

BTW: il existe des moyens plus faciles de contourner la prise d'essai dans la route express que je ne couvrirai pas dans cette réponse. Lisez à ce sujet ici: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016

L'ancienne solution

C'est la solution que j'ai publiée il y a 3 ans, car je pensais avoir une réponse qui valait la peine d'être partagée et je n'ai pas pu trouver de solution documentée ailleurs. Également dans quelques problèmes ( # 118 , # 164 , # 165 ) à node-mssql ce sujet est discuté.

Dans 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);
});

Dans 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;
};
48

Lorsque vous configurez votre application (comme lorsque vous créez le serveur express), établissez la connexion DB. Assurez-vous que cela est fait AVANT d'avoir besoin de tous vos itinéraires! (finagle le requiert en haut du fichier)

Tout comme les documents:

var sql = require('mssql'); var connection = new sql.Connection(..... //store the connection sql.globalConnection = connection;

Ensuite, dans tous vos fichiers d'itinéraire, vous pouvez le faire:

var sql = require('mssql'); var sqlConn = sql.globalConnection; var request = new sql.Request(sqlConn); //...

Ça devrait le faire!

Cela dit, utilisez knex pour gérer votre construction de requêtes MySQL. Il dispose d'un pool de connexions intégré et vous stockez l'instance knex connectée de la même manière. Ainsi qu'une généreuse portion de génial.

6
clay

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;

Voir aussi SDK MSSQL pour Node.js , Référence T-SQL , Kit de démarrage React

5
Konstantin Tarkus

J'ai utilisé un concept similaire (single connection pool), mais enveloppé la logique de connexion dans un fichier (pas besoin de passer le pool de connexions à d'autres endroits). Le connPoolPromise ci-dessous ne sera initialisé qu'une seule fois car les modules sont mis en cache après le premier chargement.

par exemple. 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);
  });

};

Utilisation user.js:

const DBUtil = require('./DBUtil');
DBUtil.query('select * from user where userId = 12', (err, recordsets) => {
  if (err) return callback(err);

  // Handle recordsets logic

}
3
Jonathan

C'est ainsi que je l'ai fait, ce qui, je pense, est un peu plus simple que certaines des autres solutions.

Fichier de base de données (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
}

Question:

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' }))
  })

EDIT: mis à jour pour correspondre à Gist de Christiaan Westerbeek qui était beaucoup plus propre.

3
ozzieisaacs

Pas fou des exemples que j'ai vus jusqu'à présent pour établir une connexion groupée. Je fais:

const pool = new mssql.ConnectionPool(msConfig).connect()
  .then(_ => { return _ } )
  .catch(e => console.error("Database Trouble!  ", e))
  
 /* ... */
 
 pool
  .then(_ => _.query( /* ... */ )
  .then(result => { /* ... */ })
  .catch(e => { /* ... */ })
0
Shawn Kelly