Как обрабатывать нетрадиционные запросы в Node.js REST API - PullRequest
5 голосов
/ 11 февраля 2020

У меня есть опыт работы PHP и front-end 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);
  });
};

...

Эта настройка работает для базовых c CRUD-операций, но что, если я захочу откатить заданный набор c пользователей и выполнить манипуляции вместо всего набора.

Например, Я хотел бы получить список числа клиентов, созданных за месяц за последние 3 месяца:

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

Должен ли я добавить в свою модель новый метод, который возвращает данные в нужном формате, или я должен добавить новый метод в контроллер, который использует метод getAll и фильтрует его в зависимости от предоставленных параметров? Я думаю, что добавление нового метода в модель может быстро выйти из-под контроля, если у меня будет много специфических c требований для запросов, однако, если я отфильтрую полный набор данных в контроллере, это кажется неэффективным, так как я Изначально извлекаю все данные из базы данных, когда я просто собираюсь игнорировать большинство из них.

Возможно, я полностью ошибаюсь, и оба эти подхода будут неправильными.

Изменить

Это дает дополнительную ясность.

Я могу добиться желаемого результата, добавив следующие фрагменты кода в мою модель и маршруты:

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.

1 Ответ

4 голосов
/ 14 февраля 2020

Я полагаю, что это действительно зависит от потребителя вашего API, и на самом деле вопрос не задается от c до NodeJS.

У вас есть ресурс /customers, а для базовых c CRUD у вас могут быть методы GET, PUT, POST, DELETE.

При беглом взгляде на RESTful API, который спроектирован таким образом, я бы предположил, что GET в конечной точке /customers вернет мне список или массив клиентов.

При создании пути /customers/monthly вы пытаетесь описать дополнительную функциональность, которая звучит подобно фильтру. Ежемесячно звучит так, будто он пытается вернуть список клиентов, отфильтрованных или сгруппированных по месяцам.

То, на что, похоже, намекают другие в комментариях, это то, что это потенциально может быть решено с использованием параметров строки запроса. Вместо того, чтобы добавить дополнительный путь к /customers, вы предлагаете что-то вроде /customers?groupBy=month.

Звучит так, будто вы хотите избежать усложнения своей модели, но я думаю, что это неизбежно, когда вы выходите за пределы базовых c операций CRUD. Вам нужно иметь какой-то способ перевести HTTP-запрос в SQL операцию.

Другой вопрос: должна ли быть ответственность модели или контроллера за выборку соответствующих данных? Модель живет ближе всего к данным, поэтому, вероятно, она должна быть ответственной.

В зависимости от того, насколько большим может стать ваш набор данных, может оказаться нецелесообразным извлекать все записи и фильтровать их на уровне контроллера. Контроллер должен действительно отвечать за перевод запроса клиента на модель, чтобы можно было получить соответствующие данные, а затем убедиться, что полученные данные отформатированы так, чтобы клиент мог их получить.

...