如何在 Node.js REST API 中处理非传统查询

Posted

技术标签:

【中文标题】如何在 Node.js REST API 中处理非传统查询【英文标题】:How to handle non traditional queries in Node.js REST APIs 【发布时间】:2020-05-26 09:00:36 【问题描述】:

我有一些 php 和前端 javascript 经验,但我正在尝试制作一个 Node.js REST API 来为我的一个应用程序的 Vue.js 客户端提供服务,但是我正在努力解决某个概念.到目前为止,我主要关注的是在线文档和指南。

我正在使用 Express.js 和 mysql 数据库。

在这个例子中,我有一个定义客户本身的客户模型,以及与数据库交互的方法。

// constructor
const Customer = function(customer) 
  this.email = customer.email;
  this.name = customer.name;
  this.active = customer.active;
  this.created_at = customer.created_at
;

Customer.getAll = result => 
  sql.query("SELECT * FROM customers", (err, res) => 
    if (err) 
      console.log("error: ", err);
      result(null, err);
      return;
    

    console.log("customers: ", res);
    result(null, res);
  );
;

...

然后我有一个客户控制器,它控制被调用的方法并在将其检索到前端之前进行任何操作。

const Customer = require("../models/customer.model.js");

exports.findAll = (req, res) => 
  Customer.getAll((err, data) => 
    if (err)
      res.status(500).send(
        message:
          err.message || "Some error occurred while retrieving customers."
      );
    else res.send(data);
  );
;

...

此设置适用于基本的 CRUD 操作,但如果我想拉回一组特定的用户并执行操作而不是整个用户组。

例如,我想检索过去 3 个月每月创建的客户数量的列表:

11/2019 200
12/2019 234
01/2020 122

我应该向我的模型添加一个以所需格式返回数据的新方法,还是应该向使用 getAll 方法并根据提供的参数过滤它的控制器添加一个新方法?如果我对查询有很多特定要求,我认为向模型添加新方法可能会很快失控,但是如果我过滤掉控制器中的全套数据,这似乎效率低下,因为我正在检索所有最初来自数据库的数据,而我将忽略其中的大部分。

可能我完全不合时宜,这两种方法都不正确。

编辑

这是为了提供额外的清晰度。

我可以通过将以下代码添加到我的模型和路由中来实现所需的输出:

Customer.getCreatedByMonth = result => 
    sql.query("select DATE_FORMAT(created_at, '%m/%Y') as month, count(*) as count from customers group by DATE_FORMAT(created_at, '%m/%Y')", (err, res) => 
        if (err) 
            console.log("error: ", err);
            result(null, err);
            return;
        

        console.log(res);
        result(null, res);
    );
;

app.get("/customers/monthly", customers.findCreatedByMonth);

虽然这感觉不是一种非常干净的方式,但我觉得我通过添加新路由及其命名/访问方式打破了一些 CRUD 约定。

我了解 SQL,理想情况下我希望避免看到 ORM,因为我仍在学习 Node。

【问题讨论】:

OT: result(null, err); 应该是 result(err, null); 您应该考虑使用query string 并扩展您的Customer.getAll 函数以接受选项(如果存在)来过滤您的结果或您想提供的任何选项。 传统上,对于上面的示例,我会使用与以下格式相同的查询:select created_date, count(name) from customers group by create_date。您的示例 (@goto1) 是否仅允许 where 条件内的选项? 所以听起来您需要端点来检索与Customer.getAll 产生的信息不同类型的信息,因此您可能应该只创建一个单独的路由和控制器来处理它。如何实现这取决于您,但是使用 Customer.getAll 会返回比您需要的更多的数据,所以如果您真的想重用东西,我仍然会接受 options 过滤/限制参数需要的东西,类似于这个例子 - sequelize.org/v5/manual/…... ... 然后,一旦您从Customer.getAll(optionsObjectHere) 获得结果,只需提取您需要的信息,并返回您需要的结果。除非我有误解,否则您可以在 query strings 中传递您想要的任何内容,并根据需要使用该信息,而不仅仅是 where 子句。 【参考方案1】:

我相信这真的取决于你的 API 的使用者,并不是 NodeJS 特有的问题。

您在/customers 有一个资源,对于基本的CRUD,您可能有GETPUTPOSTDELETE 方法。

粗略地看一下以这种方式设计的 RESTful API,我假设 /customers 端点上的 GET 将返回给我一个客户列表或数组。

在创建/customers/monthly 的路径时,您试图描述一个听起来类似于过滤器的附加功能。 Monthly 听起来像是在尝试返回经过过滤或按月分组的客户列表。

cmets 中的其他人似乎暗示的是,这可能会使用查询字符串参数来解决。与其向/customers 添加额外路径,不如提供/customers?groupBy=month 之类的内容。

听起来您想避免增加模型的复杂性,但我认为当您脱离基本的 CRUD 操作时,这是不可避免的。您需要有某种方法将 HTTP 请求转换为 SQL 操作。

另一个问题是,模型或控制器是否应该负责获取适当的数据?模型离数据最近,所以它可能应该负责。

根据数据集的大小,获取所有记录并在控制器级别进行过滤可能变得不切实际。控制器应该真正负责翻译客户端对模型的请求,以便可以检索到适当的数据,然后确保检索到的数据经过格式化,以便客户端可以接收它。

【讨论】:

以上是关于如何在 Node.js REST API 中处理非传统查询的主要内容,如果未能解决你的问题,请参考以下文章

Node JS在rest api响应中返回图像[关闭]

Node.js 通过 REST API 发送图像

如何在 Node Js 上为 Rest API 创建一个干净的架构

使用 OAuth 和 Node.JS 对 JIRA REST API 进行身份验证

编写 Node.js Rest API 的 10 个最佳实践

nodejs使用Node.js实现REST Client调用REST API