使用Next.js的API Router从MongoDB拉取数据创建API(也使用next-connect中间件)
使用Next.js的API Router可以轻松地创建简单的API!
从Next.js 9版本开始,实现了API Router功能。通过使用它,您可以完全依靠Next.js来完成以前需要使用Express.js或其他独立服务器作为API服务器的工作。
因此,我打算用MongoDB构建一个输出数据的API。
有一篇非常易懂的文章,名為「在Next.js中使用中间件而不需自定义服务器| Hoang Vo」,我希望通过解释其中的技術來介紹它,並加入自己的理解,也从中学习。
我知道有很多实施不足和误解,但是…
环境
下一个版本的 Next.js 是 9.3.4。
首先,从简单的API实验开始。
首先,我们将尝试实现一个只返回简单JSON的API。虽然官方教程中也有这个例子…
这里我们使用yarn。首先,先启动dev server。
$ yarn create next-app testapp
$ cd testapp
$ yarn dev
接下来,我们将创建一个名为pages/api/message.js的文件,并尝试创建一个仅返回简单消息的API。
export default (req, res) => {
res.status(200).json({
message: "Hello, API!",
});
};
让我们尝试调用API。
$ curl http://localhost:3000/api/message
{"message":"Hello, API!"}
只需按照以下步骤,在pages文件夹中创建名为api的文件夹(此api文件夹名称固定不可更改),然后在其中编写一个接收(req, res)参数的函数并导出,就能使其作为一个API正常运作。非常简单。
尝试连接到mongoDB
接下来,我们尝试连接到MongoDB。在这里,假设MongoDB服务器已经启动,并且testDB数据库中已经有一些users集合。
首先,我们需要添加包来引用mongoDB。
$ yarn add --dev mongodb
然后,我们将在users.js入口文件中添加一个功能来输出用户列表。
import { MongoClient } from "mongodb";
const uri = "<<各自のmongoDB接続文字列>>";
const handler = (req, res) => {
const client = new MongoClient(uri);
client.connect(async (err) => {
const collection = client.db("test").collection("users");
const users = await collection.find().toArray();
res.json(users);
});
};
export default handler;
我会尝试一下。
$curl http://localhost:3000/api/users
[{"_id":"5e8fe5fcf5577c443c17b3d3","name":"testA","password":"testApass","age":36},
"_id":"5e8fe5fcf5577c443c17b3d4","name":"testB","password":"testBpass","age":24},
"_id":"5e8fe5fcf5577c443c17b3d5","name":"testC","password":"testCpass","age":38}]
原本的获取结果是以一列显示的,现在插入了换行。
抓到了mongoDB的数据,不过,如果API不仅仅是获取用户列表,还要为其他集合创建多个入口点,那么分别编写连接程序会很麻烦呢。
因此,下一步是將DB連接設置為中間件,並在req中添加用於DB訪問的對象。使用該中間件的API可以在建立連接的狀態下專注於DB操作。
将DB连接转换为中间件化。
顺便说一句,关于中间件,如果是Express的话
app.use((req,res,next)=>{
})
app.use(cookieParser())
但是很遗憾,Next.js无法像这样做,因此我们将采用包装函数来解决这个问题。
首先,创建一个中间件目录,并在其中创建一个名为database.js的中间件文件。
import { MongoClient } from "mongodb";
const uri =
"<<各自のmongoDB接続文字列>>";
const withDatabase = (handler, collectionName) => {
return async (req, res) => {
const client = new MongoClient(uri);
await client.connect();
req.db = client.db("test").collection(collectionName);
return handler(req, res);
};
};
export default withDatabase;
几乎与之前的结构相似。
但有两个区别,第一是为了最终将其作为API运行,将其设计为返回接收(req, res)的函数;第二是在apt中的handler参数中添加了db对象,然后将该req作为参数应用到handler中。
这里的结构的形象如下。
使用此withDatabase中间件,users.js将如下所示。
import withDatabase from "../../middlewares/database";
const handler = async (req, res) => {
// withDatabaseによってreqにはdbが付与されている
const users = await req.db.find().toArray();
res.json(users);
};
export default withDatabase(handler, "users");
最后一个export的关键在于使用withDatabase封装了handler。
我感觉舒服多了。只要将这个中间件导入,我就可以创建各种API并访问数据库了。
下次连接 (Next-connect) 将会提供更加自然的表达方式。
但是,如果想要使用多个中间件的话,
export default middleA(middleB(middleC(handler)))
看起来非常复杂。
果然,就像 Express 一样。
app.use(middleA())
app.use(middleB())
app.use(middleC())
希望能够用中文写出来。
因此,我们将使用next-connect – npm。这是一个非常棒的Package,它允许在Next.js中表达类似于Express的中间件的概念。
首先,安装next-connect。
$ yarn add --dev next-connect
然后,接下来我们会修改database.js中间件的内容。
import { MongoClient } from "mongodb";
const uri = "<<自分のmongoDB接続文字列>>";
const withDatabase = (collectionName) => {
//ここの引数にnextを追加
return async (req, res, next) => {
const client = new MongoClient(uri);
await client.connect();
req.db = client.db("test").collection(collectionName);
next(); // return handler(req,res)から変更
};
};
export default withDatabase;
像是在使用next()函数结束的地方,就像是普通的Express中间件一样。
接下来,我们还需要编辑users.js文件。
import nextConnect from "next-connect"; //追加
import withDatabase from "../../middlewares/database";
const handler = nextConnect();
// useでミドルウェアを指定
handler.use(withDatabase("users"));
// このreqには、dbオブジェクトが既に付与されています
handler.get(async (req, res) => {
const users = await req.db.find().toArray();
res.json(users);
});
export default handler; // すっきりしました
通过这个方式,export default 变得更加简洁。而且,表达方式几乎与 Express 的中间件使用方式相同。如果想要使用多个中间件,只需要通过重复使用 handler.use(…) 即可实现。
在这里,就像handler.get所示,我们也可以通过handler.post创建一个用来响应客户端POST请求的API。
最后
通过使用 Next.js,我们能够轻松创建一个不需要外部服务器即可访问简单数据库的API。通过自己提供解决方案,开发变得更加容易,不是吗?
下一步,除了Hoang Vo的博客系列中介绍的在Next.js中使用中间件而无需自定义服务器外,我还想写关于使用session和passport进行认证的内容。
我参考了一篇博客文章:
博客 | Hoang Vo