web-dev-qa-db-ja.com

Express 4 Webアプリケーションの複数のルートで単一のmssql接続プールを使用するにはどうすればよいですか?

Node JS Express 4 WebアプリケーションでMSSQLデータベースコネクタとして node-mssql を使用します。ルートハンドラロジックは別のファイルで処理されます。

単一/グローバル接続プールを作成し、ルートロジックが処理される複数のファイルで使用するにはどうすればよいですか?各ルートハンドラー関数/ファイルに新しい接続プールを作成したくありません。

25

私が質問に答えてから3年が経ちました。それ以来、いくつかのことが変わりました。 ES6、mssql 4、およびExpress 4に基づいた新しいソリューションを次に示します。

ここで重要な2つの要素があります。

  1. モジュールはキャッシュされます 最初にロードされた後。つまり、require( './ db')を呼び出すたびに、まったく同じオブジェクトが返されます。 db.jsの最初のrequireは、そのファイルを実行し、promiseを作成してエクスポートします。 db.jsの2番目のrequireは、ファイルを実行せずに同じプロミスを返します。そして、それはプールで解決するという約束です。
  2. 約束は再び正当化されます。また、以前に解決した場合、最初に解決したもの(プール)ですぐに再び解決します。

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

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
}

routes/set1.jsおよび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

要約するには

モジュールのキャッシュにより、常に同じプロミスが得られ、そのプロミスは、最初に解決されたプールで何度も解決されます。したがって、各ルーターファイルは同じプールを使用します。

ところで:この回答では説明しませんが、エクスプレスルートでのtry catchを実行する簡単な方法があります。ここでそれについて読む: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016

古いソリューション

これは3年前に投稿した解決策です。共有する価値のある回答があり、文書化された解決策が他の場所で見つからなかったと信じていたからです。また、node-mssqlでのいくつかの問題( #118#164#165 )でこのトピックについて説明します。

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

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

アプリを構成するとき(エクスプレスサーバーを作成するときなど)、DB接続を確立します。すべてのルートが必要になる前に、これが完了していることを確認してください! (ファイルの先頭にあるrequireを調整します)

ドキュメントと同じように:

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

次に、すべてのルートファイルでこれを実行できます。

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

これでうまくいくはずです!

つまり、 knex を使用して、MySQLクエリの構築を管理します。接続プールが組み込まれており、接続されたknexインスタンスを同じ方法で保存します。素晴らしさの寛大な援助と同様。

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;

以下も参照してくださいMSSQL SDK for Node.jsT-SQLリファレンスReact Starter Kit

5

同様の概念(single connection pool)、ただし、接続ロジックを1つのファイルにラップしました(接続プールを他の場所に渡す必要はありません)。モジュールは最初にロードされた後にキャッシュされるため、以下のconnPoolPromiseは一度だけ初期化されます。

例えば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);
  });

};

使用法 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

これは私がそれをやった方法で、他のソリューションのいくつかよりも少し簡単だと思います。

データベースファイル(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
}

クエリ:

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

編集:Christiaan WesterbeekのGistに合わせて更新し、はるかにきれいになりました。

3
ozzieisaacs

プール接続を設定するためにこれまで見てきた例に夢中ではありません。私がやります:

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