如何使用 NodeJS 构建 REST API?什么是 API?🤔 设置项目 🛠 构建 REST API 👨‍💻 在 Heroku 上部署我们的 API

2025-05-24

如何使用 NodeJS 构建 REST API

API 是什么?🤔

设置项目

构建 REST API

在 Heroku 上部署我们的 API

👋 大家好,我知道我已经很久没发新博客了😅。👀 所以在这篇博文中,我们将构建一个 REST API,为使用 NodeJS 和 MongoDB 的开发者提供动力。那就让我们开始吧🏄‍♂️

API 是什么?🤔

API 代表“应用程序编程接口”,它是一种允许两个应用程序相互通信的工具📞。让我们通过一些实际的例子来理解 API 的含义✨

假设您构建了一个出色的电子商店应用程序,并希望其他开发者能够在此基础上构建应用程序。现在,您需要构建某种软件来在您的 Web 服务和开发者的应用程序之间进行通信,而这正是 API 发挥作用的地方。

什么是 REST API?🤔

现在,我们来谈谈“REST API”。REST 代表表述性状态转移 (Representational State Transfer),它是最广为人知的 API 架构类型之一。这类 API 遵循客户端-服务器模型,其中一个程序发送请求,另一个程序返回一些数据。
请求采用 HTTP 方法,例如 POST、GET、PUT、DELETE……

当我们构建项目时,你会对 API 和 REST API 有更清晰的理解👀。还在等什么,让我们开始深入编码吧👨‍💻。

设置项目

让我们设置我们的项目,以便我们可以开始编码👨‍💻。

  1. 为我们的项目创建一个单独的文件夹
   $ mkdir dev-credits-api
Enter fullscreen mode Exit fullscreen mode
  1. 导航到文件夹
   $ cd dev-credits-api
Enter fullscreen mode Exit fullscreen mode
  1. 初始化项目
   $ npm init
Enter fullscreen mode Exit fullscreen mode
  1. 安装所需的软件包
   $ npm install mongoose express dotenv cors

   # or

   $ yarn add mongoose express dotenv cors
Enter fullscreen mode Exit fullscreen mode
  • Express 是我们使用 REST API 的框架
  • Mongoose 是我们将用来与 MongoDB 数据库进行通信的工具

    4.1. 安装 nodemon 作为开发依赖项

     $ npm install nodemon -D
    
     # or
    
     $ yarn add nodemon -D
    
    • Nodemon 用于在检测到目录中的文件更改时自动重启服务器。这很有用,因为我们不必每次更改文件时都重启服务器。

构建 REST API

由于我们已经完成了项目的设置,让我们开始构建 REST API。

创建一个名为index.js

以下是基本 Express 应用程序的样板代码

index.js

const express = require('express');

const app = express();

const port = process.env.PORT || 3000;

app.listen(port, async () => {
  console.log(`Server is running at port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

让我们将其分解并理解每个部分:

  • 我们需要将 express 包放入我们的文件中,以便我们可以使用它
  • 我们正在为变量 port 赋值,也就是服务器运行的端口。你可能会想为什么会有这个值process.env.PORT?🤔。这是因为在 Heroku 等服务上部署时,端口号可能会发生变化,可能不是 3000,所以我们告诉它,如果存在 PORT 环境变量,则使用该值,否则使用 3000。
  • 最后一段代码告诉服务器应该监听哪个端口,在我们的例子中它是PORT变量

让我们添加一个名为 的文件start的新脚本package.json,该脚本使用 nodemon 在检测到文件更改时自动重启服务器。更改后,我们的脚本package.json将如下所示:

"scripts": {
   "start": "nodemon index.js"
}
Enter fullscreen mode Exit fullscreen mode

让我们通过运行以下npm start命令来启动服务器。服务器将运行在http://localhost:3000 上。你会看到类似这样的错误提示:

发生这种情况是因为我们没有定义/(即根路由)

HTTP 方法解释

让我们暂时放下编码,了解一下它们的作用以及成功和错误状态,以便于调试😎

得到

作用:从指定资源请求数据

成功响应:200 OK

错误响应:404 未找到

邮政

作用:向服务器发送数据以创建新资源

成功响应:201已创建

错误响应:404 未找到或 409 冲突 - 如果资源已存在

作用:向服务器发送数据以更新现有资源

成功响应:200 OK

错误响应:204 无内容、404 未找到或 405 方法不允许

删除

作用:从服务器删除资源

成功响应:200 OK

错误响应:404 未找到或 405 方法不允许

查看http.cat,通过有趣的猫咪图片了解每个 HTTP 状态代码的含义 😹

添加路线🛣

路由是 Express 应用的不同 URL 路径,与不同的 HTTP 方法相关联,例如GETPOSTDELETEPUT

让我们开始创建/发送“Hello, World!”的代码。

port在我们声明变量的行上方添加下面的代码

index.js

app.get('/', function (req, res) {
  res.send('Hello, World!');
});
Enter fullscreen mode Exit fullscreen mode

让我们分解一下这段代码:

  • get方法指定该路由的 HTTP 方法。您可以使用其他 HTTP 方法post,例如delete……
    • 有一种特殊的路由方法all,用于处理各种 HTTP 方法的路由
  • 当服务器从该端点接收到具有指定 HTTP 方法的请求时,会调用一个回调方法

🥳 好棒!“Hello, World”现在在/路线中可见了

设置 MongoDB

现在让我们进入 MongoDB 数据库😎。

前往MongoDB并注册/登录并创建一个新项目

如果您愿意的话,您可以让您的同事参与该项目。

创建项目后,点击Build a Database

您将看到如下屏幕:

让我们继续选择免费计划👀

您将看到有关云提供商和位置的更多选项

让我们选择最近的区域并继续前进。

系统会要求您创建一个用户。这是必需的,因为您需要用户名和密码来生成连接 URL,该 URL 随后将用于将 MongoDB 连接到您的 NodeJS 应用程序。

创建集群需要 1 到 3 分钟。所以,在那之前,我们先喝杯咖啡吧☕。啊……创建成功了,我们回去写代码吧👨‍💻

点击Connect

点击Connect your application

复制连接 URL

创建一个.env文件并替换<password>为您之前替换的用户密码

MONGODB_URL="mongodb+srv://kira272921:<password>@dev-credits-api.t5tkf.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
Enter fullscreen mode Exit fullscreen mode

让我们回到旧的index.js文件

将 Express 应用程序连接到 MongoDB

让我们首先mongoose要求dotenv

const mongoose = require('mongoose');
const dotenv = require('dotenv');
Enter fullscreen mode Exit fullscreen mode

让我们也配置一下 dotenv

dotenv.config();
Enter fullscreen mode Exit fullscreen mode

最后,让我们添加将 Express 应用程序连接到 MongoDB 的代码片段

mongoose
  .connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log('Connected to MongoDB');
  })
  .catch((err) => {
    console.log(err);
  });
Enter fullscreen mode Exit fullscreen mode

文件index.js显示现在看起来像这样

index.js

const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');

dotenv.config();

const app = express();

mongoose
  .connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log('Connected to MongoDB');
  })
  .catch((err) => {
    console.log(err);
  });

app.get('/', function (req, res) {
  res.send('Hello, World!');
});

const port = process.env.PORT || 3000;

app.listen(port, async () => {
  console.log(`Server is running at port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

🥳 我们已成功将 Express 应用程序连接到 MongoDB 数据库。

创建模式和模型📝

Schema 是我们数据库中文档的结构。它说明了哪些字段是必需的,以及每个字段的数据类型是什么。

模型提供了与数据库交互(读取、插入、更新等)的编程接口。

让我们创建一个名为的新文件夹model,并在其中创建一个model.js我们将定义架构的地方

model/model.js

const mongoose = require('mongoose');

const devCredits = new mongoose.Schema({
  credits: {
    type: Number,
    required: true,
  },
  id: {
    type: Number,
    required: true,
  },
});

module.exports = mongoose.model('devCredits', devCredits);
Enter fullscreen mode Exit fullscreen mode

让我们分解一下并理解

  • 我们将mongoose包导入到model/model.js文件中
  • 我们创建了一个名为 的新模式devCredits。该结构包含creditsid。Credits 是该人拥有的开发者积分数量,id 是该用户的 Discord ID(此 API 最初是为 Discord 机器人Dev credits bot创建的,因此数据库的模式有点基于 Discord 🤷‍♂️)
  • 我们最终创建了一个名为“devCredits”的模型

添加更多功能😎

让我们为 REST API 添加更多路由。让我们添加一些路由,以便我们可以通过用户的 Discord ID 获取其总开发者积分,并使用其他路由将开发者积分授予其他用户。

将开发者荣誉授予其他开发者

让我们将刚刚创建的模型导入到index.js文件中。

const devCredits = require('./model/model.js');
Enter fullscreen mode Exit fullscreen mode

让我们在index.js文件中添加一个新的 POST 路由

app.post('/post', function (req, res) {
  const credit = new devCredits({
    id: req.body.id,
    credits: req.body.credits,
  });

  devCredits.countDocuments({ id: req.body.id }, function (err, count) {
    if (count > 0) {
      devCredits.findOneAndUpdate(
        { id: req.body.id },
        {
          $inc: {
            credits: req.body.credits,
          },
        },
        { new: true },
        (err, devCredit) => {
          if (err) {
            res.send(err);
          } else res.json(devCredit);
        }
      );
    } else {
      credit.save((err, credits) => {
        if (err) {
          res.send(err);
        }
        res.json(credits);
      });
    }
  });
});
Enter fullscreen mode Exit fullscreen mode

让我们了解一下到底发生了什么:

  • 我们创建了一个新的 POST 路由(/post
  • 我们使用模型验证从客户端收到的数据
  • 在下一段代码中,我们检查用户(用户 ID)是否已存在于数据库中
    • 如果存在,我们将增加信用值
    • 否则,我们将创建一个包含用户 ID 的新文档并添加积分

如何测试 API?

我们已经成功在 API 中添加了一项新功能 🥳。不过,先别急,我们该如何测试它呢 🤔

👀 我们将使用一个名为Thunder Client的 VSCode 扩展程序,用于 API 测试。赶紧下载它,并在 API 中测试一下我们的新功能吧🥳。

下载完成后,您将在侧边栏中看到一个雷电图标👀

点击雷霆图标,你会看到类似这样的部分

点击New Request。系统将提示您进行如下操作

现在让我们测试一下我们的/post路线🥳。将输入框中的 URL 从 更改https://www.thunderclient.com/welcomeHTTP:localhost:3000/post

将 HTTP 方法从 更改GETPOST

导航到Body选项卡,这是我们要编写请求正文的部分。

我已经添加了我的 discord ID 并给了它 100 个开发者积分,因为为什么不呢

让我们点击并希望它能起作用🤞

🥁🥁🥁🥁🥁 我们收到一个错误

发生这种情况是因为我们没有任何中间件,所以让我们快速

index.js

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
Enter fullscreen mode Exit fullscreen mode

注意:我们已经将 cors 安装为单独的包,因此不要忘记导入它

现在让我们再试一次,以便它现在可以工作了🤞

🎉太棒了!我们已经成功创建了 API 中第一个与 MongoDB 数据库交互的功能。

获取用户的总开发积分

让我们将刚刚创建的模型导入到index.js文件中。

const devCredits = require('./model/model.js');
Enter fullscreen mode Exit fullscreen mode

让我们在index.js文件中添加一条新路线

app.get('/get/:id', function (req, res) {
  devCredits.find({ id: req.params.id }, { _id: 0, __v: 0 }, (err, data) => {
    if (err) {
      res.json(err);
    }
    res.json(data);
  });
});
Enter fullscreen mode Exit fullscreen mode

让我们来分析一下

  • 我们用 GET 方法创建了一条新路由
  • 我们正在数据库中查找参数中给出的 ID

让我们使用 Thunder Client 👀 再次测试一下。

🎉太棒了

清理代码库

让我们稍微清理一下代码库😅。

让我们创建一个名为的新文件夹routes,并在其中创建一个router.js包含路线的新文件

routes/router.js

const router = require('express').Router();
const devCredits = require('../model/model.js');

router.get('/get/:id', function (req, res) {
  devCredits.find({ id: req.params.id }, { _id: 0, __v: 0 }, (err, data) => {
    if (err) {
      res.json(err);
    }
    res.json(data);
  });
});

router.post('/post', function (req, res) {
  const credit = new devCredits({
    id: req.body.id,
    credits: req.body.credits,
  });

  devCredits.countDocuments({ id: req.body.id }, function (err, count) {
    if (count > 0) {
      devCredits.findOneAndUpdate(
        { id: req.body.id },
        {
          $inc: {
            credits: req.body.credits,
          },
        },
        { new: true },
        (err, devCredit) => {
          if (err) {
            res.send(err);
          } else res.json(devCredit);
        }
      );
    } else {
      credit.save((err, credits) => {
        if (err) {
          res.send(err);
        }
        res.json(credits);
      });
    }
  });
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

我们已经将routes/router.js文件导入到文件中index.js并使用了

index.js

const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const cors = require('cors');

dotenv.config();

const router = require('./routes/router.js');

const app = express();

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

mongoose
  .connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log('Connected to MongoDB');
  })
  .catch((err) => {
    console.log(err);
  });

app.get('/', function (req, res) {
  res.send('Hello, World!');
});

app.use(router);

const port = process.env.PORT || 3000;

app.listen(port, async () => {
  console.log(`Server is running at port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

让我们测试一下,以确保我们的代码和我们没有通过清理混乱而搞砸。

🥳 太棒了!没有任何错误,代码仍然像以前一样工作。

😅 看起来是不是routes/router.js有点缺乏逻辑,有点混乱?

让我们创建一个名为 的新文件夹controllers。在这个文件夹中,我们将存储与每个路由相关的逻辑。

让我们首先在controllers名为getCredits.js和的文件夹中创建一个新文件,其中包含与路由和路由postCredits.js相关的逻辑/get/post

controllers/getCredits.js

const devCredits = require('../model/model.js');

const getCredits = (req, res) => {
  devCredits.find({ id: req.params.id }, { _id: 0, __v: 0 }, (err, data) => {
    if (err) {
      res.json(err);
    }
    res.json(data);
  });
};

module.exports = getCredits;
Enter fullscreen mode Exit fullscreen mode

controllers/postCredits.js

const devCredits = require('../model/model.js');

const postCredits = (req, res) => {
  const credit = new devCredits({
    id: req.body.id,
    credits: req.body.credits,
  });

  devCredits.countDocuments({ id: req.body.id }, function (err, count) {
    if (count > 0) {
      devCredits.findOneAndUpdate(
        { id: req.body.id },
        {
          $inc: {
            credits: req.body.credits,
          },
        },
        { new: true },
        (err, devCredit) => {
          if (err) {
            res.send(err);
          } else res.json(devCredit);
        }
      );
    } else {
      credit.save((err, image) => {
        if (err) {
          res.send(err);
        }
        res.json(image);
      });
    }
  });
};

module.exports = postCredits;
Enter fullscreen mode Exit fullscreen mode

routes/router.js

const router = require('express').Router();

const devCredits = require('../model/model.js');
const getCredits = require('../controllers/getCredits.js');
const postCredits = require('../controllers/postCredits.js');

router.get('/get/:id', getCredits);

router.post('/post', postCredits);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

呼,这可真是费了不少功夫啊😹

添加速率限制

你肯定不希望某个随机的家伙把你的整个数据库都塞满垃圾邮件吧?所以,让我们给 API 添加一个速率限制,限制客户端每 x 分钟只能执行几个请求。

让我们安装express-rate-limit

$ npm install express-rate-limit

# or

$ yarn add express-rate-limit
Enter fullscreen mode Exit fullscreen mode

让我们创建一个文件夹,包含我们 API 的所有中间件。在该文件夹middleware创建一个名为rateLimiter.jsmiddleware

middleware/rateLimiter.js

const rateLimit = require('express-rate-limit');

const rateLimiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minute
  max: 10,
  message: 'Bonk 🔨',
});

module.exports = rateLimiter;
Enter fullscreen mode Exit fullscreen mode

我们来了解一下这段代码是做什么的?

  • 我们正在导入express-rate-limit
  • 指定windowMs持续时间
  • 指定max客户端在指定时间内可以发出的最大请求数量
  • message是当客户端超出最大限制时显示的消息

让我们导入index.js文件并测试一下

index.js

const rateLimiter = require('./middleware/rateLimiter.js');

app.use(rateLimiter);
Enter fullscreen mode Exit fullscreen mode

😹我被自己搞砸了

在 Heroku 上部署我们的 API

👀 我们已经成功构建了一个 API,但如果不部署它,其他开发人员如何使用它?

让我们将其部署到 Heroku 🚀 上。

首先在目录中初始化一个 git 仓库。创建一个新的 GitHub 仓库,并将你的更改推送到该仓库 👀

让我们创建一个名为 的新文件Procfile,它只是告诉 Heroku 需要运行哪个命令。将以下内容添加到Procfile文件中

web: node index.js
Enter fullscreen mode Exit fullscreen mode

注意:nodemon 在生产阶段不起作用。它只在开发阶段起作用,所以我们必须使用老方法node index.js

在Heroku上创建一个帐户并点击Create new app,给你的 API 起一些很酷的名字

前往设置选项卡并点击Reveal Config Vars

这些是环境变量

添加一个新的配置变量,其键为 as MONGODB_URL,值作为 MongoDB 连接 URL

返回部署选项卡并将之前创建的 GitHub 存储库连接到 Heroku 应用程序

点击Deploy branch按钮。TADA 🚀您已成功创建 REST API 并进行了部署 :D

本教程的完整源代码将在我的 GitHub 上提供https://github.com/Kira272921/dev-credits-api

查看我们今天构建的 API:

https://devcredits-api.herokuapp.com/

这篇博客就到这里啦🤞。下篇博文再见。

文章来源:https://dev.to/byteslash/how-to-build-a-rest-api-using-nodejs-19ai
PREV
程序员应该查看的最佳 Github 存储库
NEXT
字节大小第 2 集:图论的创建