MERN 堆栈 TODO 应用程序 [后端]

2025-06-07

MERN 堆栈 TODO 应用程序 [后端]

我们将使用MERN堆栈(MongoDB用于数据库、Express后端NodeReact前端)创建一个最小的全栈应用程序来执行CRUD操作。

我们的应用程序将允许用户

  • 创建待办事项
  • 阅读待办事项
  • 更新待办事项
  • 删除待办事项

本系列应该能让您理解CRUD使用堆栈的操作MERN

在本部分(第 1 部分)中,我们将

  • npm使用并安装必要的包初始化我们的后端
  • 设置 MongoDB 数据库
  • Node使用和设置服务器Express
  • 创建数据库模式来定义Todo
  • 设置从数据库到createread和文档update的API 路由delete
  • API使用Insomnia测试我们的路线

在我们开始之前

先决条件

应该至少对基本的编程概念有一些基本的了解,并且有一定HTML经验CSSJavaScript

这篇文章的目的并不是解释MERN堆栈,而是对如何使用它构建全栈应用程序进行了很好的介绍。

安装

  • VS Code或任何其他编辑器
  • 最新版本Node.js
  • Insomnia或邮递员
  • PrettierVS 代码扩展来格式化代码

第 1 部分:创建后端

1.初始化我们的项目

创建一个新文件夹并将其命名为您喜欢的任何名称,然后在 VS 代码中打开该文件夹并从命令提示符运行以下代码。

npm init -y
Enter fullscreen mode Exit fullscreen mode

运行此命令后,您将发现一个package.json文件夹。

2. 设置 package.json

i. 安装以下依赖项

在终端中运行以下命令来安装依赖项

npm i cors dotenv express mongoose
Enter fullscreen mode Exit fullscreen mode

cors:允许跨域 API 调用:需要从文件
dotenv访问数据:node.js 的 Web 应用程序框架:需要定义数据库模式并连接到.env
express
mongoosemongoDB

ii. 安装以下开发依赖项

现在安装以下开发依赖项,-D用于安装开发依赖项。

npm i -D nodemon
Enter fullscreen mode Exit fullscreen mode

安装依赖项后,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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,创建以下文件夹

  • 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
Enter fullscreen mode Exit fullscreen mode

iv. 将scripts以下内容更改为

"scripts": {
  "start":"node server.js",
  "dev":"nodemon server.js"
}

Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

代码如下

// 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}`);
});
view raw server.js hosted with ❤ by GitHub
// 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}`);
});
view raw server.js hosted with ❤ by GitHub

并使用以下代码启动服务器nodemon。请确保从项目目录运行以下命令。

npm run dev
Enter fullscreen mode Exit fullscreen mode

如果服务器已成功启动,则它应该在终端中显示以下消息

[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
Enter fullscreen mode Exit fullscreen mode

您也可以http://localhost:8000在浏览器上打开。

MONGO URIvi.从mongoDB获取

要连接到数据库,我们需要mongoDB集合的链接。

  1. 登录mongoDB
  2. 创建新项目
  3. 构建集群
  4. 选择云提供商
  5. 创建集群
  6. 等待集群创建。
  7. 点击连接
  8. 点击allow access from anywhere。然后Add IP address

添加连接

  1. 创建数据库用户。您需要usernamepasswordMongoDB URI
  2. 点击Choose a connection method
  3. 点击Connect your application
  4. 选择以下驱动程序和版本

    连接集群

  5. 复制mongodb+srv并粘贴到.env文件中

vii. 设置.env文件

//.env
MONGO_URI = mongodb+srv://<username>:<password>@cluster0.owmij.mongodb.net
Enter fullscreen mode Exit fullscreen mode

<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
Enter fullscreen mode Exit fullscreen mode

现在,打开文件夹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;
view raw db.js hosted with ❤ by GitHub
// 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;
view raw db.js hosted with ❤ by GitHub

在文件中添加以下更改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}`);
});
view raw server.js hosted with ❤ by GitHub
// 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}`);
});
view raw server.js hosted with ❤ by GitHub

保存更改后,它将重新启动服务器或使用命令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
Enter fullscreen mode Exit fullscreen mode
  • 进口mongoose
  • 创建一个SchemaTodoSchema
  • 我们将为待办事项添加两个字段titledescription
  • 类型titleString并且为必填字段
  • 类型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;
view raw models_todo.js hosted with ❤ by GitHub
// 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;
view raw models_todo.js hosted with ❤ by GitHub

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
Enter fullscreen mode Exit fullscreen mode

CRUD我们将定义操作的终点

  • 进口express
  • 初始化router
  • 我们稍后将从controllers
  • 为所有待办事项定义一个GET方法read
  • 定义一个POST方法用于create新的待办事项
  • PUTupdate现有待办事项定义方法
  • DELETEdelete现有待办事项定义方法
  • 导出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;
view raw todo.js hosted with ❤ by GitHub
// 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;
view raw todo.js hosted with ❤ by GitHub

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
Enter fullscreen mode Exit fullscreen mode
  • 导入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错误。

putUpdateTodofindByIdAndUpdate()需要两个参数iddata来更新待办事项。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
Enter fullscreen mode Exit fullscreen mode
  • 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;
view raw routes_todo.js hosted with ❤ by GitHub
// 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;
view raw routes_todo.js hosted with ❤ by GitHub

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
Enter fullscreen mode Exit fullscreen mode

完成后端的最后一部分是将端点添加到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}`);
});
view raw server.js hosted with ❤ by GitHub
// 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}`);
});
view raw server.js hosted with ❤ by GitHub

3 使用以下方法测试终点Insomnia

  • 创建待办事项

我们将发送POST请求至http://localhost:8000/api/todo

创建 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
    }
  }
Enter fullscreen mode Exit fullscreen mode

要更新待办事项,我们需要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
Enter fullscreen mode Exit fullscreen mode

添加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}`);
});
view raw server.js hosted with ❤ by GitHub
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}`);
});
view raw server.js hosted with ❤ by GitHub

您可以在GitHub上查看本博客的完整代码

文章来源:https://dev.to/mritunjaysaha/mern-stack-todo-application-backend-282a
PREV
什么是 RocksDB(以及它在流媒体中的作用)?
NEXT
2024 年成为更优秀软件开发人员的个人路线图