使用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中。

这里的结构的形象如下。

middleware.png

使用此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

广告
将在 10 秒后关闭
bannerAds