在本教程中,我们将使用Node.js和Express创建一个REST API。
这个API将暴露一组GET和POST端点,以允许获取数据,并发送数据进来。
我们将使用MongoDB数据库来存储这些数据。
提示:在继续学习本教程之前,确保你在系统中安装了MongoDB数据库(如果你愿意,也可以使用云MongoDB数据库)。
我们的任务是创建一个旅行费用计算器应用程序。
想象一下,在一次旅行中,你有你的应用程序(可以是一个渐进式的Web应用程序,或者一个移动应用程序,例如),在那里你可以添加你的任何费用。汽油、酒店、食物、门票等等。
当旅行结束时,你把它归档,成为历史的一部分--你可以浏览并看到你在过去的旅行中花了多少钱。
我们不打算在这里创建应用程序的前端,只是创建API。
现在让我们把它分解成细节,并把它翻译成一系列的API端点。
一个端点是一个唯一的URL,我们将调用它来创建一个操作。
就像添加一个新的行程,有它的名字。
在开始的时候,并没有存储任何行程,我们需要添加一个。我想象应用程序将询问用户的名字,并且会有一个 "创建旅行 "的按钮。当点击时,应用程序将把名字发送给我们,用POST HTTP方法发送到/trip 端点。
我们有了第一个端点,它将接受一个name 属性。
POST /trip { name }
另一个端点将列出行程,它是
GET /trips
默认情况下,它将返回按创建日期排序的行程。
当用户想添加一个新的费用时,应用程序将用POST方法调用/expense 端点,用一些参数来描述该费用
POST /expense { trip, date, amount, category, description }
trip 是当前选择的旅行的ID。
category 是该费用的类别名称。我们将提供一个可供选择的类别列表,它是静态的: , , , 。travel food accomodation fun
当我们想检索一个旅行费用时,我们用GET方法调用/expenses 端点。
GET /expenses { trip }
传递trip 的标识符。
让我们开始这个项目
我将使用Node.js的本地安装。
我们开始我们的Node.js项目,进入一个新的文件夹(称之为tripcost )并输入命令npm init -y 。
我们将使用MongoDB作为我们的数据库。
用安装mongodb Node.js包。
npm install mongodb
当你在这里的时候,还要安装Express。
npm install express
现在创建一个server.js 文件,我们将在其中存储我们的API代码,并开始要求Express和MongoDB。
const express = require("express")
const mongo = require("mongodb").MongoClient
初始化Express应用程序。
const app = express()
现在我们可以为我们支持的API端点添加存根。
app.post("/trip", (req, res) => {
/* */
})
app.get("/trips", (req, res) => {
/* */
})
app.post("/expense", (req, res) => {
/* */
})
app.get("/expenses", (req, res) => {
/* */
})
最后,使用app 上的listen() 方法来启动服务器。
app.listen(3000, () => console.log("Server ready"))
你可以使用项目文件夹中的node server.js 来运行该应用程序。
添加行程
我们为客户提供了一种使用POST /trip 端点来添加行程的方法。
app.post("/trip", (req, res) => {
/* */
})
让我们去实现它吧。
我们已经包含了MongoDB库,所以我们可以在我们的端点实现中使用它。
const mongo = require("mongodb").MongoClient
接下来,我们建立MongoDB服务器的URL。如果你在本地运行该项目,并且也在本地运行MongoDB,那么URL可能是这样的。
const url = "mongodb://localhost:27017"
因为27017 是默认的端口。
接下来,让我们使用connect() 连接到数据库。
let db
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err, client) => {
if (err) {
console.error(err)
return
}
db = client.db("tripcost")
}
)
而当我们在这里的时候,让我们也得到一个对旅行和费用集合的引用。
let db, trips, expenses
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err, client) => {
if (err) {
console.error(err)
return
}
db = client.db("tripcost")
trips = db.collection("trips")
expenses = db.collection("expenses")
}
)
现在我们可以回到我们的端点。
这个端点希望有1个参数,name ,它代表我们如何调用我们的行程。例如,瑞典2018年或优胜美地2018年8月。
请看我的教程,如何使用Express检索POST查询参数。
我们期望数据以JSON形式出现,使用Content-Type: application/json ,所以我们需要使用express.json() 中间件。
app.use(express.json())
现在我们可以通过引用Request.body 来访问数据。
app.post("/trip", (req, res) => {
const name = req.body.name
})
一旦我们有了名字,我们就可以使用trips.insertOne() 方法将行程添加到数据库中。
app.post("/trip", (req, res) => {
const name = req.body.name
trips.insertOne({ name: name }, (err, result) => {})
})
如果你得到一个类似 "无法读取未定义的属性insertOne "的错误,确保
trips在mongo.connect()中成功设置。在调用insertOne()之前添加一个console.log(trips)以确保它包含集合对象。
我们处理错误,如果在err 变量中存在,否则我们发送一个200响应(成功)给客户端,在JSON响应中添加一个ok: true 消息。
app.post("/trip", (req, res) => {
const name = req.body.name
trips.insertOne({ name: name }, (err, result) => {
if (err) {
console.error(err)
res.status(500).json({ err: err })
return
}
console.log(result)
res.status(200).json({ ok: true })
})
})
这就是了!
现在通过点击ctrl-C ,重新启动Node应用程序,并再次运行它。
你可以使用Insomnia应用程序来测试这个端点,这是一个测试和互动REST端点的好方法。
列出行程
行程列表是由GET /trips 端点返回的。它不接受任何参数。
app.get("/trips", (req, res) => {
/* */
})
我们已经初始化了trips 集合,所以我们可以直接访问它来获得列表。
我们使用trips.find() 方法,其结果我们必须使用toArray() 转换为一个数组。
app.get("/trips", (req, res) => {
trips.find().toArray((err, items) => {})
})
然后我们可以处理err 和items 的结果。
app.get("/trips", (req, res) => {
trips.find().toArray((err, items) => {
if (err) {
console.error(err)
res.status(500).json({ err: err })
return
}
res.status(200).json({ trips: items })
})
})
下面是Insomnia中API调用的结果。
增加一项开支
我们之前得到了旅行的列表。每个行程都有一个相关的_id 属性,当它被添加时,MongoDB会直接添加。
{
"trips": [
{
"_id": "5bdf03aed64fb0cd04e15728",
"name": "Yellowstone 2018"
},
{
"_id": "5bdf03c212d45cdb5ccec636",
"name": "Sweden 2017"
},
{
"_id": "5bdf047ccf4f42dc368590f6",
"name": "First trip"
}
]
}
我们将使用这个_id 来注册一个新的费用。
如果你还记得,添加新费用的端点是这样的。
POST /expense { trip, date, amount, category, description }
trip 在这种情况下,将是我们之前注册的一个旅行的 。想象一下,在应用程序中,用户将添加一次旅行,这将保持为当前的旅行,直到添加(或选择)新的旅行。_id
让我们继续下去,实现我们的存根。
app.post("/expense", (req, res) => {
/* */
})
就像添加旅行时一样,我们将使用insertOne()方法,这次是在expenses 集合。
我们从请求体中得到5个参数。
-
trip -
date日期,ISO 8601格式(例如: ),GMT时区2018-07-22T07:22:13 -
amount一个整数,包含金额 -
category这是一个来自 , , ,travelfoodaccomodationfun -
description对费用的描述,以便我们以后能记住它。app.post("/expense", (req, res) => { expenses.insertOne( { trip: req.body.trip, date: req.body.date, amount: req.body.amount, category: req.body.category, description: req.body.description, }, (err, result) => { if (err) { console.error(err) res.status(500).json({ err: err }) return } res.status(200).json({ ok: true }) } ) })
列出所有费用
拼图的最后一块是获取费用。
我们需要填写/expenses 端点的存根,这是最后一个缺失的端点。
app.get("/expenses", (req, res) => {
/* */
})
这个端点接受trip 参数,它是存储在数据库中的旅行的_id 属性。
app.get("/expenses", (req, res) => {
expenses.find({ trip: req.body.trip }).toArray((err, items) => {
if (err) {
console.error(err)
res.status(500).json({ err: err })
return
}
res.status(200).json({ expenses: items })
})
})
这个Insomnia的截图显示了它的工作情况。