Node+Express+MongoDB 服务端开发小结
在刚过去的一次Hackathon里面,写了一个服务端的应用。不过由于时间关系,并没有写完 Orz。不过呢,也趁着这个机会学到了一些新的东西。下面就来具体介绍一下吧~
服务端技术栈的选择
服务端之所以选择Node,显而易见,是因为JavaScript圈的生态。之前也有用过Python+Flask/Django写过后端,有些地方就不如Node,比如异步、回调、箭头函数等。Express相当于Python里面的Flask,能够简化一些服务端的写法,比如初始化服务、路由等。之前在用Node做项目的时候,数据库我一直避开了,因为考虑到自己的项目对存取没有太大要求,只需要方便操作就行了。因此我用了lowdb,一个基于json的本地数据库。你的数据库不大的话,那么用这样一个方式就可以了,而且读取json非常的便捷。此外还用过SQLite,这是本地的SQL数据库,查询需要写SQL。这次的项目里尝试了一下MongoDB,一个非关系型数据库,基于对象和文档进行存储。初始化Node及Express
首先我们$npm init
一个空项目出来,添加express库$yarn add express
,新建一个server.js
,像下面一样添加几行代码,就能实现一个最基本的服务。访问localhost:3000/
就能看到Hello World的输出。const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Listening on port ${port}!`))
初始化数据库
首先我们需要在服务器或本地安装MongoDB。我是在Windows平台上安装的,安装包体积大约有500M,网上有很多教程,中间有一个步骤会问你是否需要安装MongoDB Compass,建议可以安装一下,能够可视化的调试数据库。Windows上的话需要手动去启动MongoDB的服务,服务默认的端口是27017。在Node项目中,我们需要使用相关的库来对数据库进行操作,这里我用了Mongoose。可以很优雅的连接MongoDB的数据库。我创建了一个db.js
模块,将数据库作为一个module。可以看到我连接的是MongoDB里面的test
数据库。var mongoose = require("mongoose");
let DB_URL = "mongodb://localhost:27017/test";
mongoose.connect(
DB_URL,
{ useNewUrlParser: true }
);
mongoose.connection.on("connected", function() {
console.log("Mongoose connection open to " + DB_URL);
});
module.exports = mongoose;
项目结构
组件化是很有必要的,特别是当项目逐渐变得复杂的时候。于是我用了这样一个结构,将各个功能之间的耦合程度降低。routes
文件夹存放不同任务的路由,models
文件夹存放MongoDB中对象的定义,controllers
里面是对于不同类型数据的增删改查的操作。因此整个目录就像下面这样:├── controllers
│ ├── taskController.js
│ └── userController.js
├── db.js
├── models
│ ├── task.js
│ └── user.js
├── package.json
├── routes
│ ├── task.js
│ └── user.js
├── server.js
└── yarn.lock
数据模型的构建
因为使用了MongoDB,根据Mongoose提供的对象化的操作接口,需要将数据模型抽象出来。以post.js
为例,我们将post抽象出来,创建一个schema,告诉数据库一个post都有哪几个部分,每个部分对应的类型。这样的好处是在数据库设计时就能考虑到不同数据之间的关系,并将它们嵌起来,比如post中嵌入评论段。MongoDB的好处就是像对象一样管理数据,减少查询的次数。但值得注意的是,如果关系更复杂的话(之后我就遇到了这个问题),嵌套数据反而很麻烦。否则就得像传统SQL设计数据表一样,但会造成很多的信息冗余。这个问题可能是后期还需要进一步探索的。var mongoose = require("mongoose");
var Schema = mongoose.Schema;
let PostSchema = new Schema({
id: String,
title: String,
type: String,
author: String,
sendTime: Date,
tags: [String],
content: String
});
module.exports = {
schema: PostSchema,
model: mongoose.model("Post", PostSchema)
};
数据控制器的实现
对于不同的请求,我们需要根据请求的内容来对数据库进行存取,并返回对应的值。首先我们将数据库模型实例化,再根据后面路由调用的函数进行相应的操作。具体的查找筛选可以参考mongoose的文档。总之数据模型的层级不能太深,否则查找起来非常的难受,就会怀念SQL。var GroupInstance = require("../models/group").model;
var TaskInstance = require("../models/task").model;
// Task index
exports.index = (req, res) => res.send("This is task index");
// List all users
exports.list_all = (req, res) => {
GroupInstance.find({}, (err, items) => res.json({ tasks: items }));
};
路由的实现
这个还是相对比较简单的。需要用到不容的控制器,并定义不同路由所触发的对应控制器的函数。再将其导出成模块供我们主服务使用。像task.js
:var express = require("express");
var router = express.Router();
var taskController = require("../controllers/taskController");
router.get("/", taskController.index);
router.get("/list-all", taskController.list_all);
router.post("/new", taskController.new);
module.exports = router;
模块的整合
到最后,我们就能将所有的模块整合在一起,此时我们的主文件server.js
就应该如下。其中我们还使用了 body-parser
模块,来解析post请求时发送的数据。引入不同的router,并定义他们的前缀,这样就可以通过这样的方式进行请求:localhost:3000/task/new
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const port = 3000;
// Parser middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Declare routers
var indexRouter = require("./routes/index");
var userRouter = require("./routes/user");
var taskRouter = require("./routes/task");
// Use different routers
app.use("/", indexRouter);
app.use("/user", userRouter);
app.use("/task", taskRouter);
app.listen(port, () => console.log(`Listening on port ${port}!`));
调试热重载
我们不可能每次都手动去启动我们的服务,我们可以使用nodemon
模块来实现热重载,这样每次修改文件都能自动的启动服务。我们可以在 package.json
里面这样写:"scripts": {
"dev": "nodemon server.js"
}
这样我们就可以用命令 yarn dev
来启动一个开发版本的服务了。
Comments
Post a Comment