上一个: 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。接下来,我们将连接控制器,实体和模型之间的点。