Изначально это было размещено на моем веб-сайте: Обслуживание данных с помощью ExpressJS.

Получение наших данных

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

База данных - это то, о чем мы поговорим в следующей части этой серии. А пока я собираюсь взять те же данные, которые были у нас в службе данных, и вставить их в файл на сервере, который мы можем извлечь и обслуживать через API-маршрут.

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

Поэтому вместо того, чтобы сразу вводить базу данных, мы будем строить с жестко закодированными данными, но имейте в виду, что у нас будет база данных в будущем и мы будем строить соответственно (то, что я имею в виду под этим, станет очевидным, это несколько абзацев).

Получите данные

Найдите данные здесь

Я начну с создания файла с именем data.js. Сами данные представляют собой три массива, каждый из которых соответствует фактам о черепахах, вопросам викторины и ответам на эти вопросы соответственно. Мы экспортируем три бита данных, поместив следующий код в data.js:

module.exports = {
  turtlesData,
  quizQuestions,
  correctAnswers,
}

Помните о будущих особенностях

На этом этапе было бы заманчиво создать несколько маршрутов для обслуживания данных, просто импортировать данные из data.js и отправлять их обратно при каждом запросе пользователя. Очевидно, это будет работать нормально. Но я упоминал выше, что нам нужно иметь в виду, что мы скоро представим базу данных.

Проблема с простым импортом данных и их отправкой обратно в том, что это синхронное событие, а наша будущая функция (добавление базы данных) - это асинхронное действие. Если мы просто импортируем и отправим обратно данные, когда мы фактически добавим базу данных, нам придется изменить весь код, который мы только что написали, чтобы теперь использовать асинхронную функцию, которая вызывает базу данных. Эта функция, скорее всего, будет использовать обещания, чтобы справиться с асинхронным характером запроса.

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

Для этого мы могли бы создать несколько функций, которые имитируют асинхронный характер запроса к базе данных, используя обещания, которые немедленно преобразуются в данные из data.js.

Функции модели

Создайте папку с именем models/, в которую мы поместим эти функции. Внутри я создал два файла: fact.js для обработки данных, относящихся к разделу фактов приложения, и questions.js для обработки данных для самой викторины.

Заглянув в файл fact.js, мы можем импортировать массив turtlesData из data.js следующим образом:

const { turtlesData } = require('../data');

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

function getTurtleData() {
  return Promise.resolve(turtlesData);
}

Очевидно, что этот запрос на самом деле не является асинхронным, но с помощью Promise.resolve() мы можем имитировать реальное асинхронное событие. Теперь, когда мы вызываем функцию getTurtleData, мы можем рассматривать ее как реальное асинхронное событие, которое использует обещания и вызывает .then() для получения возвращаемых из него данных.

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

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

Экспорт функции

Теперь, когда у нас есть заглушенная функция, мы должны ее экспортировать. В результате наш файл models / fact.js будет выглядеть следующим образом:

// models/facts.js
const { turtlesData } = require('../data');

module.exports = {
  getTurtleData,
}

function getTurtleData() {
  return Promise.resolve(turtlesData);
}

Используйте функцию модели в маршруте

У нас есть данные и функция модели для извлечения этих данных с помощью интерфейса обещания. Теперь давайте используем это в маршруте для передачи данных клиенту.

Внутри файла routes/api.v0.js мы можем добавить вызов router.get для прослушивания запросов GET к маршруту '/ data' и предоставления данных фактов.

router.get('/data', dataHandler);

function dataHandler(req, res) {
  // code here
}

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

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

Импортировать функцию модели

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

Вверху файла /routes/api.v0.js добавьте следующее:

const { getTurtleData } = require('../models/facts');

У нас есть доступ к функции модели, поэтому теперь мы можем реализовать функцию dataHandler.

function dataHandler(req, res) {
  getTurtleData()
    .then(data => {
      res.json(data);
    });
}

Мы использовали .then, потому что создали функцию getTurtleData для возврата обещания. Затем мы используем метод res.json, чтобы вернуть данные в формате json пользователю.

Вы видели res.send в более ранней части этого руководства, и res.json очень похож, но он правильно отформатирует ответ как json, тогда как res.send не делает никаких предположений о типе данных. Поэтому, если вы знаете, что хотите вернуть json, лучше использовать res.json.

Теперь весь файл routes / api.v0.js выглядит так

// routes/api.v0.js
const express = require('express');
const router = express.Router();

const { getTurtleData } = require('../models/facts');

router.get('/data', dataHandler);

module.exports = router;

function dataHandler(req, res) {
  getTurtleData()
    .then(data => {
      res.json(data);
    });
}

Проверяя это в браузере, перейдя по адресу http: // localhost: 8080 / api / data, мы видим следующий вывод:

Кстати, результат выглядит не очень хорошо, мы можем изменить это, добавив его в основной файл приложения после строки const app = express():

app.set('json spaces', 2);

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

Тем не менее, это показывает вам новый метод объекта приложения - app.set(). Что можно использовать для установки глобальных параметров конфигурации.

Промыть и повторить

Просто повторите тот же процесс, чтобы создать функции модели в файле models/questions.js и создать обработчики маршрутов для возврата этих данных.

Я просто покажу вам конечный результат файлов здесь:

// models/questions.js
const { correctAnswers, quizQuestions } = require('../data');

module.exports = {
  getCorrectAnswers,
  getQuizQuestions,
}

function getCorrectAnswers() {
  return Promise.resolve(correctAnswers);
}

function getQuizQuestions() {
  return Promise.resolve(quizQuestions);
}
// routes/api.v0.js
const express = require('express');
const router = express.Router();

const { getTurtleData } = require('../models/facts');
const { getCorrectAnswers, getQuizQuestions } = require('../models/questions');

router.get('/data', dataHandler);
router.get('/questions', questionHandler);
router.get('/answers', answerHandler);

module.exports = router;

function dataHandler(req, res) {
  getTurtleData()
    .then(data => {
      res.json(data);
    });
}

function questionHandler() {
  getQuizQuestions()
    .then(data => {
      res.json(data);
    });
}

function answerHandler() {
  getCorrectAnswers()
    .then(data => {
      res.json(data);
    });
}

Это будет красиво отображать вопросы и ответы, когда мы перейдем к http: // localhost: 8080 / api / questions и http: // localhost: 8080 / api / answers соответственно:

Таким образом, мы реализовали модель данных и предоставили эти данные клиенту.

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

Оставайтесь голодными и продолжайте кодировать.

Адриан

Пожалуйста, поделитесь этим постом, если он вам понравился. Каждому нужен этот замечательный друг, чтобы присылать им потрясающие вещи.

Первоначально опубликовано на https://hungryturtlecode.com.