原文地址:https://www.thepolyglotdeveloper.com/2019/02/using-hapijs-mongoose-mongodb-build-rest-api/
在本教程中,我们将使用Hapi.js,Joi和Mongoose作为后端框架以及MongoDB作为NoSQL数据库来开发一个简单的RESTful API 。通过简化功能并验证客户端提供的数据。
创建Hapi.js项目
创建一个新的项目目录并执行以下命令,并安装支持数据验证的joi和连接mongoDB数据库的mongoose依赖模块:
npm init -y
npm install hapi joi mongoose --save
上面的命令将创建一个新的package.json文件,并安装Hapi.js框架,Joi验证框架和Mongoose对象文档建模器(ODM)。
我们将所有应用程序代码添加到单个项目文件中。创建一个app.js文件,包含以下JavaScript代码:
const Hapi = require("hapi");
const Mongoose = require("mongoose");
const Joi = require("joi");
const server = new Hapi.Server({ "host": "localhost", "port": 3000 });
server.route({
method: "POST",
path: "/person",
options: {
validate: {}
},
handler: async (request, h) => {}
});
server.route({
method: "GET",
path: "/people",
handler: async (request, h) => {}
});
server.route({
method: "GET",
path: "/person/{id}",
handler: async (request, h) => {}
});
server.route({
method: "PUT",
path: "/person/{id}",
options: {
validate: {}
},
handler: async (request, h) => {}
});
server.route({
method: "DELETE",
path: "/person/{id}",
handler: async (request, h) => {}
});
server.start();
我们已经在app.js文件中添加了很多代码。实质上,我们已经导入了下载的依赖项,定义了服务器设置,定义了也称为接口的路由,并启动了服务器。
您会注意到,并非我们的所有路由都是一样的。我们正在开发基于创建,检索,更新和删除(CRUD)的REST API,并在一些接口上进行了验证。特别是,我们将向接口添加验证逻辑,以将数据保存到数据库,而不是检索或删除。
接下来,让我们看一下如何配置MongoDB并添加我们的接口逻辑。
使用Mongoose ODM与数据库进行交互
记住,我假设您已经可以访问MongoDB实例。在定义服务器配置之后,在app.js文件的顶部,我们需要连接到MongoDB。插入以下行以建立连接:
Mongoose.connect("mongodb://localhost/thepolyglotdeveloper");
您需要将连接字符串信息换成您自己的连接字符串信息。在使用Mongoose时,我们需要为每个集合定义一个模型。由于这是一个简单的示例,因此我们只有一个模型,它看起来如下所示:
const PersonModel = Mongoose.model("person", {
firstname: String,
lastname: String
});
我们的每个文档都包含一个firstname和一个lastname,但是两个字段都不是必需的。这些文档将保存到people ODM模型的复数形式的集合中。
此时,可以使用MongoDB了。
现在是时候开始开发我们的API接口了,因此从创建接口开始,我们可能会有类似这样的内容:
server.route({
method: "POST",
path: "/person",
options: {
validate: {}
},
handler: async (request, h) => {
try {
var person = new PersonModel(request.payload);
var result = await person.save();
return h.response(result);
} catch (error) {
return h.response(error).code(500);
}
}
});
我们现在已经跳过了验证逻辑,但是在我们内部,handler我们将接收与客户端请求一起发送的有效负载数据,并创建模型的新实例。使用我们的模型,我们可以保存到数据库并将响应返回给客户端。mongoose将基于schema对有效负载数据进行基本验证,但我们可以做得更好。这是在Hapi.js中安装Joi模块的原因。
让我们看一下validate路线中的对象:
validate: {
payload: {
firstname: Joi.string().required(),
lastname: Joi.string().required()
},
failAction: (request, h, error) => {
return error.isJoi ? h.response(error.details[0]).takeover() : h.response(error).takeover();
}
}
在validate对象中,我们选择验证payload。我们还可以选择验证请求的params以及query,但此处都不需要。尽管我们可以执行一些非常复杂的验证,但我们只是在验证两个属性是否都存在。我们不会使用缺失的错误返回给用户一个模糊的错误,而是使用failActionwhich是可选的来返回确切的错误。
现在,让我们看一下如何检索已创建的数据。在典型的CRUD方案中,我们可以检索所有数据或特定数据。我们将适应两种情况。
server.route({
method: "GET",
path: "/people",
handler: async (request, h) => {
try {
var person = await PersonModel.find().exec();
return h.response(person);
} catch (error) {
return h.response(error).code(500);
}
}
});
上面的路由将find在Mongoose中执行该函数,而无需任何查询参数。这意味着没有条件可搜索从集合返回所有数据的结果。同样,我们可以返回特定的数据。
如果要返回特定的数据,可以在find函数中提供参数,也可以使用以下命令:
server.route({
method: "GET",
path: "/person/{id}",
handler: async (request, h) => {
try {
var person = await PersonModel.findById(request.params.id).exec();
return h.response(person);
} catch (error) {
return h.response(error).code(500);
}
}
});
在上述接口中,我们接受id路由参数并正在使用该findById函数。从交互返回的数据将返回给面向客户端的应用程序。
随着创建和检索接口的完成,我们可以以更新和删除接口结束本教程。从更新终结点开始,我们可能会有类似以下内容:
server.route({
method: "PUT",
path: "/person/{id}",
options: {
validate: {
payload: {
firstname: Joi.string().optional(),
lastname: Joi.string().optional()
},
failAction: (request, h, error) => {
return error.isJoi ? h.response(error.details[0]).takeover() : h.response(error).takeover();
}
}
},
handler: async (request, h) => {
try {
var result = await PersonModel.findByIdAndUpdate(request.params.id, request.payload, { new: true });
return h.response(result);
} catch (error) {
return h.response(error).code(500);
}
}
});
就像创建接口一样,我们正在验证数据。但是,我们的验证与先前的接口有点不同。并不是说我们的属性是必需的,我们只是说它们是可选的。当我们执行此操作时,显示的任何不在验证器中的属性都将引发错误。因此,例如,如果我想包含一个中间名,它将失败。
在handler函数内部,我们可以使用称为的快捷函数findByIdAndUpdate,该函数使我们能够找到文档以相同的操作进行更新和更新,而不是分两步进行。我们包括new设置,以便可以将最新的文档信息返回给客户端。
删除接口将简单得多:
server.route({
method: "DELETE",
path: "/person/{id}",
handler: async (request, h) => {
try {
var result = await PersonModel.findByIdAndDelete(request.params.id);
return h.response(result);
} catch (error) {
return h.response(error).code(500);
}
}
});
使用id从客户端传递的参数,我们可以执行findByIdAndDelete函数,该函数将通过id查找文档,然后一举删除它,而不用分两步进行。
到目前为止,您应该已经可以使用该API。在尝试与Angular或Vue.js之类的前端框架一起使用之前,您可能想要使用Postman之类的工具进行接口调试。