it-swarm.com.de

Wie kann ich einen einzelnen mssql-Verbindungspool über mehrere Routen in einer Express 4-Webanwendung verwenden?

Ich möchte node-mssql als MSSQL-Datenbank-Connector in einer Node JS Express 4-Webanwendung verwenden. Die Routing-Handler-Logik wird in separaten Dateien behandelt.

Wie erstelle ich einen einzelnen/globalen Verbindungspool und verwende ihn für mehrere Dateien, in denen die Routenlogik verarbeitet wird? Ich möchte nicht in jeder Route-Handler-Funktion/Datei einen neuen Verbindungspool erstellen.

25

Es ist 3 Jahre her, seit ich die Frage gestellt und beantwortet habe. Seitdem haben sich einige Dinge geändert. Hier ist die neue Lösung basierend auf ES6, MSSQL 4 und Express 4, die ich heute vorschlagen würde.

Hier spielen zwei Schlüsselelemente eine Rolle.

  1. Module werden zwischengespeichert nach dem ersten Laden. Dies bedeutet, dass jeder anzufordernde Aufruf ('./db') genau dasselbe Objekt zurückgibt. Bei der ersten Anforderung von db.js wird diese Datei ausgeführt, das Versprechen erstellt und exportiert. Die zweite Anforderung von db.js gibt DIESES Versprechen zurück, ohne die Datei auszuführen. Und dieses Versprechen wird sich mit dem Pool auflösen.
  2. Ein Versprechen kann erneut bestätigt werden. Und wenn es zuvor aufgelöst wurde, wird es sofort mit dem, was es beim ersten Mal aufgelöst hat, dem Pool, wieder aufgelöst.

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

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

Im routes/set1.js und 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

Um zusammenzufassen

Durch das Zwischenspeichern von Modulen erhalten Sie immer das gleiche Versprechen, und dieses Versprechen wird immer wieder mit dem Pool aufgelöst, den es beim ersten Mal aufgelöst hat. Somit verwendet jede Router-Datei denselben Pool.

Übrigens: Es gibt einfachere Möglichkeiten, den Versuchsfang auf der Expressroute vorzunehmen, auf die ich in dieser Antwort nicht eingehen werde. Lesen Sie darüber hier: https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016

Die alte Lösung

Dies ist die Lösung, die ich vor 3 Jahren gepostet habe, weil ich der Meinung war, eine Antwort zu haben, die es wert war, geteilt zu werden, und ich konnte an keiner anderen Stelle eine dokumentierte Lösung finden. Auch in einigen Ausgaben ( # 118 , # 164 , # 165 ) bei node-mssql wird dieses Thema behandelt.

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

Im 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

Wenn Sie Ihre App konfigurieren (wie beim Erstellen des Express-Servers), stellen Sie die DB-Verbindung her. Stellen Sie sicher, dass dies erfolgt ist, BEVOR Sie alle Ihre Routen benötigen! (Finagle die Anforderungen am oberen Rand der Datei)

Genau wie die Dokumente:

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

Dann können Sie in all Ihren Routendateien Folgendes tun:

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

Das sollte es tun!

Verwenden Sie knex , um Ihre MySQL-Abfragen zu verwalten. Es verfügt über einen integrierten Verbindungspool, und Sie speichern die verbundene knex-Instanz auf dieselbe Weise. Sowie eine großzügige Portion Super.

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;

Siehe auch MSSQL SDK für Node.js , T-SQL-Referenz , React Starter Kit

5

Ich habe ein ähnliches Konzept verwendet (single connection pool), aber die Verbindungslogik in eine Datei eingeschlossen (Verbindungspool muss nicht an andere Stellen übergeben werden). Das unten stehende connPoolPromise wird nur einmal initialisiert, da Module nach dem ersten Laden zwischengespeichert werden.

z.B. 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);
  });

};

Verwendung 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

So habe ich es gemacht, was meiner Meinung nach ein bisschen einfacher ist als einige der anderen Lösungen.

Datenbankdatei (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
}

Abfrage:

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

BEARBEITEN: Aktualisiert, um Christiaan Westerbeeks Kern zu entsprechen, der viel sauberer war.

3
ozzieisaacs

Ich bin nicht verrückt nach den Beispielen, die ich bisher für das Einrichten einer gepoolten Verbindung gesehen habe. Ich mache:

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