将node.js后端与mongoDB集成
#node #express #mongodb #mongoose

上一个: Create modular routes with express

现在,我们已经使用express.js设置了一个node.js后端,并了解我们要定义控制器和实体的方式,现在该使我们的设置更加现实了。我们现在的后端缺少一个非常关键的方面 - 持久性(或简单地说,它需要一个数据库)。

忠于我们的堆栈,让我们整合MongoDB。此时,我们应该下载并安装MongoDB服务器。您可以使用此链接:MongoDB Community Server

要充分理解事情,我们必须举一个例子。这是我们要做的:

我们的应用程序保留了电影的数据库及其评级。因此,我们正在与三个实体打交道:用户(您或我),电影(需要我们的时间,金钱和关注的东西)和评分(用户和电影之间的链接)。

步骤1-获取猫鼬

mongoDB是一个示意性数据库,这意味着您可以将任何JSON存储在任何集合中,而新的围观者可能会绝对吸引。
输入mongoose-一个提供模式和模型以使在MongoDB上处理数据的库。

让我们导航到我们的node.js project root,然后执行此

npm i --save mongoose

我们的软件包。

    "dependencies": {
        "express": "4.18.2",
        "helmet": "6.0.1",
        "link-module-alias": "1.2.0",
        "mongoose": "6.9.2",
        "winston": "3.8.2"
    }

步骤2-设置数据访问

稍微回到图纸板,让我们在项目中建立一个数据访问模块(文件夹)。

创建一个数据访问文件夹
mkdir ./data-access

data-access再次是您在应用程序代码中,即使在嵌套的地方也可能需要的东西。让我们将其设置为以绝对路径的访问。在package.json_moduleAliases部分中添加一个条目:

    "_moduleAliases": {
        "_controllers": "./controllers",
        "_entities": "./entities",
        "_utils": "./utils",
        "_config": "./config",
        "_data-access": "./data-access"
    }

完成后,我们必须通过执行
来创建新目录的链接 npm i

在控制台输出中注意:

> link-module-alias

link-module-alias: _controllers -> ./controllers, _entities -> ./entities, _utils -> ./utils, _config -> ./config, _data-access -> ./data-access

步骤3-点亮它的时间

让我们理解一些微不足道的东西 - 如果数据库连接不起作用,则应用程序将处于受损状态。因此,在其他任何内容之前,我们的node.js进程应连接到数据库,然后启动应用程序。

假设我们正在使用MongoDB的本地副本,让我们修改./config/index.js文件以包含数据库详细信息:

...
    DATABASE: {
        URL: 'mongodb://127.0.0.1',
        DB_NAME: 'moviemania'
    }

我们的示例应用程序非常简单,但是现实世界的应用程序可能与多个数据库和多个模型一起使用。考虑到这一点,最好是建立数据连接工厂。我们的实现利用了工厂设计模式和 singleton设计模式。创建一个文件./data-access/ConnectionFactory.js为:

const { DATABASE: { URL, DB_NAME } } = require('_config');
const mongoose = require('mongoose');
/**
 * @type {mongoose.Connection}
 */
let dbConnection;

module.exports = {
    getDBConnection: function () {
        if (!dbConnection) {
            dbConnection = mongoose.createConnection(URL, { dbName: DB_NAME, maxPoolSize: 10 });
        }
        return dbConnection;
    },
    closeConnection: function () {
        if (dbConnection && dbConnection.readyState === 1) {
            dbConnection.close();
        }
    }
};

我们将不得不将我们的index.js重构为:

  • 一个连接到数据库的功能
  • 启动应用程序的另一个后续功能
const logger = require('_utils/logger');

function connectToDB() {
    const { getDBConnection } = require('_data-access/ConnectionFactory');
    const connection = getDBConnection();
    return new Promise((resolve, reject) => {
        let counter = 1;
        const timer = setInterval(function () {
            logger.info(`[${counter}] Checking connection..`);
            if (connection.readyState === 1) {
                clearInterval(timer);
                resolve();
            } else if (counter === 5) {
                clearInterval(timer);
                reject('Could not connect to database after 5 retries');
            }
        }, 1000);
    });
}

function setupApp() {
    const {
        SERVER: { PORT, REQUEST_BODY_SIZE_LIMIT },
    } = require('_config');
    const { getController } = require('_controllers');
    const helmet = require('helmet');
    const express = require('express');
    const entities = require('_entities');
    const app = express();

    app.use((req, res, next) => {
        logger.log('info', `Received request [${req.method}] ${req.originalUrl}`);
        next();
    });

    app.use(helmet());
    app.use(express.json({ limit: REQUEST_BODY_SIZE_LIMIT }));
    app.use(express.urlencoded({ extended: true, limit: REQUEST_BODY_SIZE_LIMIT }));

    app.get('/healthcheck', (req, res, next) => {
        res.send('OK');
    });

    app.use(...getController(entities.getEntityOne()));
    const logger = require('_utils/logger');

function connectToDB() {
    const { getDBConnection } = require('_data-access/ConnectionFactory');
    const connection = getDBConnection();
    return new Promise((resolve, reject) => {
        let counter = 1;
        const timer = setInterval(function () {
            logger.info(`[${counter}] Checking connection..`);
            if (connection.readyState === 1) {
                clearInterval(timer);
                resolve();
            } else if (counter === 5) {
                clearInterval(timer);
                reject('Could not connect to database after 5 retries');
            }
        }, 1000);
    });
}

function setupApp() {
    const {
        SERVER: { PORT, REQUEST_BODY_SIZE_LIMIT },
    } = require('_config');
    const { getController } = require('_controllers');
    const helmet = require('helmet');
    const express = require('express');
    const entities = require('_entities');
    const app = express();

    app.use((req, res, next) => {
        logger.log('info', `Received request [${req.method}] ${req.originalUrl}`);
        next();
    });

    app.use(helmet());
    app.use(express.json({ limit: REQUEST_BODY_SIZE_LIMIT }));
    app.use(express.urlencoded({ extended: true, limit: REQUEST_BODY_SIZE_LIMIT }));

    app.get('/healthcheck', (req, res, next) => {
        res.send('OK');
    });

    app.use(...getController(entities.getEntityOne()));
    app.use('/', (req, res) => {
        res.send(`${req.originalUrl} can not be served`);
    });

    app.listen(PORT, () => {
        logger.log('info', `Listening on port ${PORT}`);
    });
}

connectToDB()
    .then(function () {
        logger.info('Connected to database, starting app now...');
        setupApp();
    })
    .catch(function (e) {
        logger.error('Failed to connect to database.', e);
    });
    app.use(...getController(entities.getUserEntity()));

    app.use('/', (req, res) => {
        res.send(`${req.originalUrl} can not be served`);
    });

    app.listen(PORT, () => {
        logger.log('info', `Listening on port ${PORT}`);
    });
}

connectToDB()
    .then(function () {
        logger.info('Connected to database, starting app now...');
        setupApp();
    })
    .catch(function (e) {
        logger.error('Failed to connect to database.', e);
    });

如果您仔细注意到,我们的connectToDB功能会返回承诺。只有在实现承诺时,才会调用下一个函数setupApp

因此,我们已经设置了一个node.js后端并连接到mongoDB。接下来,我们将连接控制器,实体和模型之间的点。

下一步: Working with Node.js Entities and Mongoose Models - I