MERN 堆栈 TODO 应用程序 [后端]
我们将使用MERN堆栈(MongoDB用于数据库、Express后端Node和React前端)创建一个最小的全栈应用程序来执行CRUD操作。
我们的应用程序将允许用户
- 创建待办事项
- 阅读待办事项
- 更新待办事项
- 删除待办事项
本系列应该能让您理解CRUD使用堆栈的操作MERN。
在本部分(第 1 部分)中,我们将
- npm使用并安装必要的包初始化我们的后端
- 设置 MongoDB 数据库
- Node使用和设置服务器- Express
- 创建数据库模式来定义Todo
- 设置从数据库到create、read和文档update的API 路由delete
- API使用Insomnia测试我们的路线
在我们开始之前
先决条件
应该至少对基本的编程概念有一些基本的了解,并且有一定的HTML经验CSS。JavaScript
这篇文章的目的并不是解释MERN堆栈,而是对如何使用它构建全栈应用程序进行了很好的介绍。
安装
- VS Code或任何其他编辑器
- 最新版本Node.js
- Insomnia或邮递员
- PrettierVS 代码扩展来格式化代码
第 1 部分:创建后端
1.初始化我们的项目
创建一个新文件夹并将其命名为您喜欢的任何名称,然后在 VS 代码中打开该文件夹并从命令提示符运行以下代码。
npm init -y
运行此命令后,您将发现一个package.json文件夹。
2. 设置 package.json
i. 安装以下依赖项
在终端中运行以下命令来安装依赖项
npm i cors dotenv express mongoose
cors:允许跨域 API 调用:需要从文件dotenv访问数据:node.js 的 Web 应用程序框架:需要定义数据库模式并连接到.envexpressmongoosemongoDB
ii. 安装以下开发依赖项
现在安装以下开发依赖项,-D用于安装开发依赖项。
npm i -D nodemon
安装依赖项后,package.json文件夹应如下所示。
// package.json
{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}
 iii. 将main入口点更改为server.js
现在,创建一个server.js文件和一个.env。该server.js文件将作为服务器的入口点,并且.env将包含MONGO_URI。我们还必须在 中进行以下更改package.json
//package.json
{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "server.js", //changed
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}
现在,创建以下文件夹
- 
  config:在config文件夹中,创建一个名为的文件db.js。此文件将包含连接数据库所需的代码MongoDB。
- 
  controllers:该controllers文件夹将包含端点与数据库通信的方法的文件。
- 
  models:该models文件夹将包含定义MongoDB schema
- 
  routers:该routers文件夹将包含扩展名为 的文件endpoints。
在此阶段,文件结构应如下所示
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
 iv. 将scripts以下内容更改为
"scripts": {
  "start":"node server.js",
  "dev":"nodemon server.js"
}
该package.json文件应如下所示
{
  "name": "mern-todo",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js", //added
    "dev": "nodemon server.js" //added
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "config": "^3.3.6",
    "cors": "^2.8.5",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "mongoose": "^5.13.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.11"
  }
}
v. 设置服务器
我们将执行以下操作来设置服务器
- 进口express
- 使用初始化我们的应用程序express()
- 使用以下get方法为端点设置方法http://localhost:8000app.get()
- PORT将我们的服务器设置- 8000为运行
- PORT使用我们的应用程序来收听- app.listen()
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json
代码如下
| // server.js | |
| const express = require("express"); | |
| const app = express(); | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
| // server.js | |
| const express = require("express"); | |
| const app = express(); | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
并使用以下代码启动服务器nodemon。请确保从项目目录运行以下命令。
npm run dev
如果服务器已成功启动,则它应该在终端中显示以下消息
[nodemon] 2.0.11
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node server.js`
server is running on http://localhost:8000
您也可以http://localhost:8000在浏览器上打开。
 MONGO URIvi.从mongoDB获取
要连接到数据库,我们需要mongoDB集合的链接。
- 登录mongoDB
- 创建新项目
- 构建集群
- 选择云提供商
- 创建集群
- 等待集群创建。
- 点击连接
- 点击allow access from anywhere。然后Add IP address
- 创建数据库用户。您需要username和password。MongoDB URI
- 点击Choose a connection method
- 点击Connect your application
- 
  选择以下驱动程序和版本 
- 
  复制 mongodb+srv并粘贴到.env文件中
 vii. 设置.env文件
//.env
MONGO_URI = mongodb+srv://<username>:<password>@cluster0.owmij.mongodb.net
将<username>和替换<password>为您在步骤 9 中设置的数据库用户名和密码。
viii. 连接数据库
.
├── config
│   └── db.js <-- we are here
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
现在,打开文件夹db.js中的文件config并添加以下更改。
- 进口mongoose
- 导入MONGO_URI自.env
- 定义connectDB连接数据库的方法
- 导出connectDB要调用的方法server.js
| // config/db.js | |
| const mongoose = require("mongoose"); | |
| const db = process.env.MONGO_URI; | |
| const connectDB = async () => { | |
| try { | |
| await mongoose.connect(db, { | |
| useNewUrlParser: true, | |
| useUnifiedTopology: true, | |
| }); | |
| console.log("MongoDB is connected"); | |
| } catch (err) { | |
| console.error(err.message); | |
| process.exit(1); | |
| } | |
| }; | |
| module.exports = connectDB; | 
| // config/db.js | |
| const mongoose = require("mongoose"); | |
| const db = process.env.MONGO_URI; | |
| const connectDB = async () => { | |
| try { | |
| await mongoose.connect(db, { | |
| useNewUrlParser: true, | |
| useUnifiedTopology: true, | |
| }); | |
| console.log("MongoDB is connected"); | |
| } catch (err) { | |
| console.error(err.message); | |
| process.exit(1); | |
| } | |
| }; | |
| module.exports = connectDB; | 
在文件中添加以下更改server.js。
- 进口dotenv
- 导入connectDB方法来自config/db.js
- 调用该connectDB方法。
让我们在server.js
| // server.js | |
| require("dotenv").config(); //added | |
| const express = require("express"); | |
| const connectDB = require("./config/db"); //added | |
| const app = express(); | |
| // connect database | |
| connectDB();//added | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
| // server.js | |
| require("dotenv").config(); //added | |
| const express = require("express"); | |
| const connectDB = require("./config/db"); //added | |
| const app = express(); | |
| // connect database | |
| connectDB();//added | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
保存更改后,它将重新启动服务器或使用命令npm run dev。终端应该显示我们在try块下MongoDB is connected添加的消息。db.js
ix. 定义数据库模式
在 models 文件夹中创建一个todo.js文件。我们将在此文件中定义数据库模式。
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js <-- we are here
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
- 进口mongoose
- 创建一个Schema叫TodoSchema
- 我们将为待办事项添加两个字段title;description
- 类型title为String并且为必填字段
- 类型description为,String并且不是必填字段
- 导出模型
代码如下
| // models/todo.js | |
| const mongoose = require("mongoose"); | |
| const TodoSchema = new mongoose.Schema({ | |
| title: { | |
| type: "String", | |
| required: true, | |
| }, | |
| description: { | |
| type: "String", | |
| }, | |
| }); | |
| const Todo = mongoose.model("todo", TodoSchema); | |
| module.exports = Todo; | 
| // models/todo.js | |
| const mongoose = require("mongoose"); | |
| const TodoSchema = new mongoose.Schema({ | |
| title: { | |
| type: "String", | |
| required: true, | |
| }, | |
| description: { | |
| type: "String", | |
| }, | |
| }); | |
| const Todo = mongoose.model("todo", TodoSchema); | |
| module.exports = Todo; | 
x. 定义终点
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js <-- we are here
├── .env
├── server.js
├── package-lock.json
└── package.json
CRUD我们将定义操作的终点
- 进口express
- 初始化router
- 我们稍后将从controllers
- 为所有待办事项定义一个GET方法read
- 定义一个POST方法用于create新的待办事项
- PUT为- update现有待办事项定义方法
- DELETE为- delete现有待办事项定义方法
- 导出router
代码如下
| // router/todo.js | |
| const express = require("express"); | |
| const router = express.Router(); | |
| /** | |
| * @route GET api/todo | |
| * @description get all todo | |
| * @access public | |
| */ | |
| router.get("/"); | |
| /** | |
| * @route POST api/todo | |
| * @description add a new todo | |
| * @access public | |
| */ | |
| router.post("/"); | |
| /** | |
| * @route PUT api/todo/:id | |
| * @description update todo | |
| * @access public | |
| */ | |
| router.put("/:id"); | |
| /** | |
| * @route DELETE api/todo/:id | |
| * @description delete todo | |
| * @access public | |
| */ | |
| router.delete("/:id"); | |
| module.exports = router; | 
| // router/todo.js | |
| const express = require("express"); | |
| const router = express.Router(); | |
| /** | |
| * @route GET api/todo | |
| * @description get all todo | |
| * @access public | |
| */ | |
| router.get("/"); | |
| /** | |
| * @route POST api/todo | |
| * @description add a new todo | |
| * @access public | |
| */ | |
| router.post("/"); | |
| /** | |
| * @route PUT api/todo/:id | |
| * @description update todo | |
| * @access public | |
| */ | |
| router.put("/:id"); | |
| /** | |
| * @route DELETE api/todo/:id | |
| * @description delete todo | |
| * @access public | |
| */ | |
| router.delete("/:id"); | |
| module.exports = router; | 
xi. 定义终点方法
controllers我们将在文件夹中定义端点的方法
.
├── config
│   └── db.js
├── controllers
│   └── todo.js <-- we are here
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js
├── package-lock.json
└── package.json
- 导入Todo模型models/todo
- 定义以下四个方法 
  - getAllTodo
- postCreateTodo
- putUpdateTodo
- deleteTodo
 
- 导出所有方法
| // controllers/todo.js | |
| const Todo = require("../models/todo"); | |
| exports.getAllTodo = (req, res) => { | |
| Todo.find() | |
| .then((todo) => res.json(todo)) | |
| .catch((err) => | |
| res | |
| .status(404) | |
| .json({ message: "Todo not found", error: err.message }) | |
| ); | |
| }; | |
| exports.postCreateTodo = (req, res) => { | |
| Todo.create(req.body) | |
| .then((data) => res.json({ message: "Todo added successfully", data })) | |
| .catch((err) => | |
| res | |
| .status(400) | |
| .json({ message: "Failed to add todo", error: err.message }) | |
| ); | |
| }; | |
| exports.putUpdateTodo = (req, res) => { | |
| Todo.findByIdAndUpdate(req.params.id, req.body) | |
| .then((data) => res.json({ message: "updated successfully", data })) | |
| .catch((err) => | |
| res | |
| .status(400) | |
| .json({ message: "Failed to update todo", error: err.message }) | |
| ); | |
| }; | |
| exports.deleteTodo = (req, res) => { | |
| Todo.findByIdAndRemove(req.params.id, req.body) | |
| .then((data) => | |
| res.json({ message: "todo deleted successfully", data }) | |
| ) | |
| .catch((err) => | |
| res | |
| .status(404) | |
| .json({ message: "book not found", error: err.message }) | |
| ); | |
| }; | 
| // controllers/todo.js | |
| const Todo = require("../models/todo"); | |
| exports.getAllTodo = (req, res) => { | |
| Todo.find() | |
| .then((todo) => res.json(todo)) | |
| .catch((err) => | |
| res | |
| .status(404) | |
| .json({ message: "Todo not found", error: err.message }) | |
| ); | |
| }; | |
| exports.postCreateTodo = (req, res) => { | |
| Todo.create(req.body) | |
| .then((data) => res.json({ message: "Todo added successfully", data })) | |
| .catch((err) => | |
| res | |
| .status(400) | |
| .json({ message: "Failed to add todo", error: err.message }) | |
| ); | |
| }; | |
| exports.putUpdateTodo = (req, res) => { | |
| Todo.findByIdAndUpdate(req.params.id, req.body) | |
| .then((data) => res.json({ message: "updated successfully", data })) | |
| .catch((err) => | |
| res | |
| .status(400) | |
| .json({ message: "Failed to update todo", error: err.message }) | |
| ); | |
| }; | |
| exports.deleteTodo = (req, res) => { | |
| Todo.findByIdAndRemove(req.params.id, req.body) | |
| .then((data) => | |
| res.json({ message: "todo deleted successfully", data }) | |
| ) | |
| .catch((err) => | |
| res | |
| .status(404) | |
| .json({ message: "book not found", error: err.message }) | |
| ); | |
| }; | 
getAllTodo:该find()方法将返回集合中的所有待办事项。如果集合为空,则返回404错误。
postCreateTodo:该create()方法将创建一个待办事项并返回成功消息。否则,它将返回400错误。
putUpdateTodo:findByIdAndUpdate()需要两个参数id和data来更新待办事项。id参数 将从 中提取req.params.id。
deleteTodo:该findByIdAndRemove()方法只需要一个参数,即id待办事项。
xii. 将方法添加到端点
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js <-- we are here
├── .env
├── server.js
├── package-lock.json
└── package.json
- CRUD导入操作方法
- 将方法添加到端点
| // routes/todo.js | |
| const express = require("express"); | |
| const router = express.Router(); | |
| const { | |
| getAllTodo, | |
| postCreateTodo, | |
| putUpdateTodo, | |
| deleteTodo, | |
| } = require("../controllers/todo"); | |
| /** | |
| * @route GET api/todo | |
| * @description get all todo | |
| * @access public | |
| */ | |
| router.get("/", getAllTodo); | |
| /** | |
| * @route POST api/todo | |
| * @description add a new todo | |
| * @access public | |
| */ | |
| router.post("/", postCreateTodo); | |
| /** | |
| * @route PUT api/todo/:id | |
| * @description update todo | |
| * @access public | |
| */ | |
| router.put("/:id", putUpdateTodo); | |
| /** | |
| * @route DELETE api/todo/:id | |
| * @description delete todo | |
| * @access public | |
| */ | |
| router.delete("/:id", deleteTodo); | |
| module.exports = router; | 
| // routes/todo.js | |
| const express = require("express"); | |
| const router = express.Router(); | |
| const { | |
| getAllTodo, | |
| postCreateTodo, | |
| putUpdateTodo, | |
| deleteTodo, | |
| } = require("../controllers/todo"); | |
| /** | |
| * @route GET api/todo | |
| * @description get all todo | |
| * @access public | |
| */ | |
| router.get("/", getAllTodo); | |
| /** | |
| * @route POST api/todo | |
| * @description add a new todo | |
| * @access public | |
| */ | |
| router.post("/", postCreateTodo); | |
| /** | |
| * @route PUT api/todo/:id | |
| * @description update todo | |
| * @access public | |
| */ | |
| router.put("/:id", putUpdateTodo); | |
| /** | |
| * @route DELETE api/todo/:id | |
| * @description delete todo | |
| * @access public | |
| */ | |
| router.delete("/:id", deleteTodo); | |
| module.exports = router; | 
 xiii. 在server.js
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json
完成后端的最后一部分是将端点添加到server.js文件中。
- 进口routes/todo.js
- 将路由端点添加到中间件
| // server.js | |
| require("dotenv").config(); | |
| const express = require("express"); | |
| const connectDB = require("./config/db"); | |
| const app = express(); | |
| // routes | |
| const todo = require("./routes/todo"); // added | |
| // connect database | |
| connectDB(); | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // use routes | |
| app.use("/api/todo", todo); // added | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
| // server.js | |
| require("dotenv").config(); | |
| const express = require("express"); | |
| const connectDB = require("./config/db"); | |
| const app = express(); | |
| // routes | |
| const todo = require("./routes/todo"); // added | |
| // connect database | |
| connectDB(); | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // use routes | |
| app.use("/api/todo", todo); // added | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
 3 使用以下方法测试终点Insomnia
- 创建待办事项
我们将发送POST请求至http://localhost:8000/api/todo
- 阅读待办事项
我们将发送GET请求至http://localhost:8000/api/todo
您可以在mongoDB中检查更改collections
- 更新待办事项
要更新待办事项,我们将发送PUT请求至http://localhost:8000/api/todo/id
必须id从服务器的响应消息中获取。
  {
    "message": "Todo added successfully",
    "data": {
      "_id": "60ec0f9655f9735a60a2d967",
      "title": "test todo",
      "description": "test todo",
      "__v": 0
    }
  }
要更新待办事项,我们需要id。我们将从预览选项卡的 获取 。id使用request和request 后,我们可以从获取。_ididpreviewGETPOST
- 删除待办事项
要删除待办事项,我们将发送DELETE请求至http://localhost:8000/api/todo/id
 4. 添加cors
.
├── config
│   └── db.js
├── controllers
│   └── todo.js
├── models
│   └── todo.js
├── node_modules
├── routes
│   └── todo.js
├── .env
├── server.js <-- we are here
├── package-lock.json
└── package.json
添加cors后,我们就可以从前端应用程序(如 react)进行 api 调用。
| require("dotenv").config(); | |
| const express = require("express"); | |
| const cors = require("cors"); // added | |
| const connectDB = require("./config/db"); | |
| const app = express(); | |
| // routes | |
| const todo = require("./routes/todo"); | |
| // connect database | |
| connectDB(); | |
| // cors | |
| app.use(cors({ origin: true, credentials: true })); // added | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // use routes | |
| app.use("/api/todo", todo); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
| require("dotenv").config(); | |
| const express = require("express"); | |
| const cors = require("cors"); // added | |
| const connectDB = require("./config/db"); | |
| const app = express(); | |
| // routes | |
| const todo = require("./routes/todo"); | |
| // connect database | |
| connectDB(); | |
| // cors | |
| app.use(cors({ origin: true, credentials: true })); // added | |
| // initialize middleware | |
| app.use(express.json({ extended: false })); | |
| app.get("/", (req, res) => res.send("Server up and running")); | |
| // use routes | |
| app.use("/api/todo", todo); | |
| // setting up port | |
| const PORT = process.env.PORT || 8000; | |
| app.listen(PORT, () => { | |
| console.log(`server is running on http://localhost:${PORT}`); | |
| }); | 
您可以在GitHub上查看本博客的完整代码
文章来源:https://dev.to/mritunjaysaha/mern-stack-todo-application-backend-282a 后端开发教程 - Java、Spring Boot 实战 - msg200.com
            后端开发教程 - Java、Spring Boot 实战 - msg200.com
          




