Как перенести ваше приложение с Express на Fastify

Fastify Программирование и разработка

Express уже давно является самым популярным фреймворком для разработки веб-приложений с помощью Node.js. К сожалению, в последние годы этот фреймворк не претерпел большого активного развития. Это означает, что он не поддерживает современные функции JavaScript. Тем временем появился ряд новых фреймворков, которые используют другой подход к разработке приложений Node.js. Один из таких фреймворков — Fastify.

В этой статье мы рассмотрим, что делает Fastify привлекательной альтернативой для разработки веб-приложений с помощью Node.js. Мы узнаем, как избежать необходимости переписывать существующие приложения Express с нуля и вместо этого поэтапно переводить их на использование Fastify. К тому времени, когда мы закончим, вы сможете уверенно перенести существующие приложения Express и начать использовать преимущества инфраструктуры Fastify.

Вместе с этой статьей вы должны следовать нескольким требованиям:

  • Вам нужно будет уметь создавать базовое приложение Express, определять маршруты и настраивать промежуточное ПО.
  • А также нужно будет удобно запускать команды в терминале.
  • Вам потребуется установить Node.js > = v14.13.0. Это обеспечивает нам хорошую поддержку модулей ECMAScript (ES) и позволяет использовать ожидание верхнего уровня.
  • В примерах кода в этой статье используется синтаксис модуля ES ( import/ export).

Каковы преимущества перехода с Express на Fastify?

Если вам удобно создавать приложения Node.js с помощью Express, вам может быть интересно, какие преимущества дает перенос существующих приложений Express в Fastify. Вот несколько веских причин, по которым стоит задуматься о переезде:

  • Проверка и выход из коробки. Эти функции обычно требуются при создании веб-приложений. При использовании Fastify нет необходимости выбирать и интегрировать библиотеки для этих задач, поскольку он предоставляет их нам. Мы узнаем больше об этих функциях позже в этой статье.
  • Встроенная поддержка асинхронного кода. Fastify изначально обрабатывает обещания и поддерживает async/ await. Это означает, что маршруты будут ловить нереализованные отклоненные обещания для нас. Это позволяет нам безопасно писать асинхронный код. Это также позволяет нам делать изящные вещи, например автоматически отправлять возвращаемое значение из функции обработчика маршрута в качестве тела ответа:
app.get("/user/:id", async (request) => await getUser(request.params.id));
  • Автоматический синтаксический анализ и сериализация JSON. Нам не нужно настраивать Fastify для анализа тел запросов JSON или сериализации объектов как JSON для ответов. Он обрабатывает все это автоматически:
app.get("/user/:id", async (request, reply) => {
  const name = request.body.name;

  reply.send({ user: { name } });
});
  • Удобство для разработчиков. Благодаря явным и выразительным API-интерфейсам, а также отличной поддержке TypeScript, Fastify был разработан с учетом опыта разработчиков.
  • Это быстро. Мы никогда не хотим, чтобы фреймворк стал источником узких мест в производительности наших приложений. Хорошая новость заключается в том, что Fastify создан для обеспечения высокой производительности. В Fastify тесты показывают, как он сравнивает с другими фреймворк Node.js.
    В активной разработке. Фреймворк Fastify активно развивается. Есть регулярные выпуски с улучшениями и исправлениями ошибок / безопасности.
Читайте также:  События указателя в Javascript DOM?

Как перенести API с уверенностью

Мы хотим быть уверены, что наше приложение по-прежнему работает должным образом после того, как оно было перенесено на Fastify. Одна из вещей, которые могут помочь нам отловить ошибки или выявить непреднамеренные изменения, — это тесты интеграции API.

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

Если мы пишем тесты интеграции API для приложения Express, мы хотим иметь возможность запускать те же тесты после миграции приложения на Fastify. При написании интеграционных тестов для API необходимо учитывать несколько ключевых моментов:

  • Они не должны быть привязаны к определенной структуре. Мы хотим иметь возможность запускать одни и те же тесты до и после миграции без необходимости изменять тесты или какие-либо библиотеки, которые мы используем для них.
  • Сделайте их простыми. Как минимум, интеграционные тесты должны делать запросы к конечным точкам, которые предоставляет API, и проверять, что ответ возвращается, но обычно не более того. Возможно, мы захотим проверить конкретные коды состояния HTTP или заголовки ответов, но мы должны постараться, чтобы тесты были как можно более простыми.
  • Выберите инструменты, которые вам удобны. Существует множество различных инструментов, которые могут помочь нам в создании и запуске тестов API, но важно использовать инструменты, которые нам удобны. Чтобы написать эффективные интеграционные тесты, нам нужно иметь возможность делать HTTP-запросы и делать утверждения на основе ответов нашего API. В общем, нам не нужно много библиотек или инструментов, чтобы эта работа работала.

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

Переход от Express к Fastify с помощью fastify-express

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

fastify-expressПлагин добавляет полную совместимость Экспресс Fastify. Он предоставляет use()метод, который мы можем использовать для добавления промежуточного программного обеспечения Express и маршрутов к нашему серверу Fastify. Это дает нам возможность постепенно переносить части существующего приложения Express в Fastify.

Вот пример экспресс-роутера :

// src/routes.js
const router = express.Router();

router.get("/:user_id", function getUser(request, response, next) {
  response.json({});
});

export default router;

Затем мы можем использовать fastify-expressдля добавления нашего существующего маршрутизатора Express к экземпляру сервера Fastify:

// src/server.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

const fastify = Fastify();

await fastify.register(ExpressPlugin);

fastify.use("/user", routes);

await fastify.listen(3000);

Мы подробно рассмотрим, как все это работает, немного позже, когда начнем переносить наше приложение на Fastify.

Важно знать, что использование fastify-expressплагина не является долгосрочным решением. Если мы хотим получить все преимущества Fastify, нам нужно будет в какой-то момент перенести код нашего приложения, специфичного для Express. Однако fastify-expressплагин дает нам возможность поэтапного перехода на Fastify.

Наш пример экспресс-заявки

Мы собираемся создать пример Express-приложения, а затем перенести его на платформу Fastify. Давайте теперь посмотрим на его код.

Обязательные зависимости

Сначала создадим новый проект:

mkdir express-to-fastify-migration
cd express-to-fastify-migration
npm init -y

Затем мы запустим эту команду в нашем терминале, чтобы установить зависимости, которые потребуются нашему Express-приложению:

npm install express cors

Наконец, откройте package.jsonи добавьте следующую строку над scriptsразделом:

"type": "module",

Это позволит нам загружать модули ES в наше приложение.

Модуль маршрутизатора

Мы собираемся создать экземпляр маршрутизатора Express, чтобы помочь нам инкапсулировать наши маршруты и промежуточное ПО. Маршрутизаторы в Express можно использовать, чтобы помочь нам организовать наше приложение в отдельные модули. Например, у нас может быть один маршрутизатор для /userмаршрутов и другой маршрутизатор для /addressмаршрутов. Позже мы увидим, как это может помочь нам поэтапно перенести наше приложение Express на Fastify.

Давайте создадим экземпляр маршрутизатора и добавим к нему промежуточное ПО :

// src/routes.js

import express from "express";
import cors from "cors";

const router = express.Router();

router.use(express.json());

router.use(cors({ origin: true }));

В приведенном выше коде мы настроили два примера промежуточного программного обеспечения Express:

  • Eexpress.json(). Эта функция промежуточного программного обеспечения встроена в Express. Он обрабатывает тела запросов JSON.
  • Сors. Это промежуточное ПО помогает нам добавлять заголовки CORS в наши ответы API. Это позволит вызывать наш API с веб-страницы.

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

Теперь, когда мы настроили промежуточное ПО, мы можем добавить первый маршрут к нашему маршрутизатору:

// src/routes.js

router.post("/", function createUser(request, response, next) {
  const newUser = request.body;

  if (!newUser) {
    return next(new Error("Error creating user"));
  }

  response.status(201).json(newUser);
});

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

Теперь добавим маршрут для получения пользователя:

// src/routes.js

router.get("/:user_id", function getUser(request, response, next) {
  const user = {
    id: request.params.user_id,
    first_name: "Bobinsky",
    last_name: "Oso",
  };

  response.json(user);
});

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

Наконец, мы экспортируем routerобъект, чтобы мы могли импортировать его в другой модуль:

// src/routes.js

export default router;

Модуль приложения

Теперь мы собираемся создать модуль приложения:

// src/app.js

import express from "express";

import routes from "./routes.js";

export default function buildApp() {
  const app = express();

  app.use("/user", routes);

  return app;
}

В этом модуле мы определяем функцию, которая создает новый экземпляр сервера Express. Затем мы добавляем наш объект маршрутизатора к экземпляру сервера.

Серверный модуль

Наконец, мы создадим серверный модуль. Этот модуль использует buildApp()функцию, которую мы определили в нашем модуле приложения, для создания нового экземпляра сервера Express. Затем он запускает наш экспресс-сервер, настраивая его на прослушивание порта 3000:

// src/server.js

import buildApp from "./app.js";

const express = buildApp();

express.listen(3000, () => {
  console.log("Example app listening at http://localhost:3000");
});

Запуск нашего приложения

Теперь у нас есть полностью работающее экспресс-приложение, которое мы можем запустить в нашем терминале:

node src/server.js

В отдельном терминале мы можем сделать запрос к API с помощью cURL, чтобы убедиться, что он работает:

curl --verbose --request GET \
  --url http://localhost:3000/user/3d395cb4-531c-4989-b8ed-9cc75198187e \
  --header 'Origin: http://example-origin.com'

Мы должны получить ответ, который выглядит так:

< HTTP/1.1 200 OK
< X-Powered-By: Express
< Access-Control-Allow-Origin: http://example-origin.com
< Vary: Origin
< Content-Type: application/json; charset=utf-8
< 

{"id":"3d395cb4-531c-4989-b8ed-9cc75198187e","first_name":"Bobinsky","last_name":"Oso"}

Перенос нашего приложения с Express на Fastify

Теперь, когда у нас есть полнофункциональное приложение Express, мы собираемся перенести его на использование фреймворка Fastify.

Обязательные зависимости

Нам нужно установить три зависимости:

  • Fastify framework
  • fastify-экспресс плагин
  • в fastify-CORS плагин — который является портом Экспресс corsпромежуточного слоя, что наше приложение уже использует

Давайте запустим эту команду в нашем терминале, чтобы установить их:

npm install fastify fastify-express fastify-cors

Рефакторинг нашего модуля приложения

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

  • импорт fastifyи fastify-expressвместоexpress
  • создать экземпляр сервера Fastify вместо экземпляра сервера Express
  • используйте fastify-expressплагин, чтобы добавить наш объект Express Router на сервер

Вот как это выглядит после того, как мы внесли эти изменения:

// src/app.js

import Fastify from "fastify";
import ExpressPlugin from "fastify-express";

import routes from "./routes.js";

export default async function buildApp() {
  const fastify = Fastify({
    logger: true,
  });

  await fastify.register(ExpressPlugin);

  fastify.use("/user", routes);

  return fastify;
}

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

Рефакторинг нашего серверного модуля

Теперь нам нужно изменить наш серверный модуль для работы с экземпляром сервера Fastify:

// src/server.js

import buildApp from "./app.js";

const fastify = await buildApp();

try {
  await fastify.listen(3000);
} catch (error) {
  fastify.log.error(error);
  process.exit(1);
}

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

Следующие шаги

Наше приложение теперь использует Fastify для маршрутизации запросов и отправки ответов. Он полностью функционален, но Express все еще используется на наших маршрутах. Чтобы полностью отказаться от Express, нам необходимо также перенести наши маршруты на использование Fastify.

Рефакторинг нашего модуля маршрутов

Маршруты в нашем приложении Express инкапсулируются в маршрутизатор Express. Мы собираемся преобразовать этот маршрутизатор в плагин Fastify. Плагины — это функция Fastify, которая позволяет нам инкапсулировать маршруты и любые связанные с ними функции.

Мы начнем рефакторинг нашего модуля routes ( src/routes.js), удалив некоторые специфичные для Express строки:

  • — import express from «express»
  • — const router = express.Router();
  • — router.use(express.json());

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

export default async function routes(fastify) {
  // Configure routes
}

Чтобы наше промежуточное ПО и маршруты работали с Fastify, нам нужно изменить:

  • router ссылки на fastify
  • функции обработчика маршрута async
  • аргументы функции обработчика маршрута от (request, response, next)до(request, reply)
  • response ссылки на reply
  • звонки response.json()вreply.send()
  • экземпляры next(error)вthrow error

После внесения всех этих изменений наш модуль маршрутов теперь является плагином Fastify, содержащим маршруты Fastify:

// src/routes.js

import cors from "cors";

export default async function routes(fastify) {
  fastify.use(cors({ origin: true }));

  fastify.post("/", async function createUser(request, reply) {
    const newUser = request.body;

    if (!newUser) {
      throw new Error("Error creating user");
    }

    reply.status(201).send(newUser);
  });

  fastify.get("/:user_id", async function getUser(request, reply) {
    const user = {
      id: request.params.user_id,
      first_name: "Bobinsky",
      last_name: "Oso",
    };

    reply.send(user);
  });
}

Теперь нам нужно изменить наш модуль приложения ( src/app.js), чтобы использовать плагин, который мы экспортируем из модуля маршрутов. Это означает замену fastify.use()звонка на звонок fastify.register():

-  fastify.use("/user", routes);
+  fastify.register(routes, { prefix: "/user" });

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

Замена промежуточного программного обеспечения плагинами

Наше приложение в хорошей форме, и мы почти полностью перевели его с Express на Fastify. Осталось перенести одну вещь: использование нами corsпакета промежуточного программного обеспечения Express. Мы установили fastify-corsплагин ранее, и теперь нам нужно добавить его в наше приложение, чтобы заменить corsпромежуточное ПО.

В модуль наши маршруты ( src/routes.js), нам нужно заменить importна ПО corsпромежуточного слоя:

-  import cors from "cors";
+  import CorsPlugin from "fastify-cors";

Затем нам нужно заменить вызов на fastify.use()вызов fastify.register():

-  fastify.use(cors({ origin: true }));
+  fastify.register(CorsPlugin, { origin: true });

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

Поскольку мы больше не используем use()функцию, fastify-expressпредоставляемую плагином, мы можем полностью удалить ее из нашего приложения. Для этого удалим следующие строки из нашего модуля приложения ( src/app.js):

-  import ExpressPlugin from "fastify-express";

-  await fastify.register(ExpressPlugin);

Удаление зависимостей Express

Миграция нашего приложения с Express на Fastify завершена! Теперь мы можем удалить зависимости, связанные с Express, выполнив эту команду в нашем терминале:

npm uninstall express cors fastify-express

Запуск нашего перенесенного приложения

Теперь, когда мы полностью перенесли наше приложение на Fastify, самое время убедиться, что все по-прежнему работает так, как мы ожидали. Давайте запустим те же команды, которые мы запускали ранее, когда наше приложение использовало Express.

Сначала запустим приложение в нашем терминале:

node src/server.js

Затем в отдельном терминале мы сделаем запрос к API с помощью cURL, чтобы убедиться, что он работает должным образом:

curl --verbose --request GET \
  --url http://localhost:3000/user/3d395cb4-531c-4989-b8ed-9cc75198187e \
  --header 'Origin: http://example-origin.com'

Мы должны получить ответ, который выглядит так:

< HTTP/1.1 200 OK
< vary: Origin
< access-control-allow-origin: http://example-origin.com
< content-type: application/json; charset=utf-8
< 

{"id":"3d395cb4-531c-4989-b8ed-9cc75198187e","first_name":"Bobinsky","last_name":"Oso"}

Отход от промежуточного программного обеспечения

В нашем примере приложения Express использовалось только несколько функций промежуточного программного обеспечения, но в наших реальных приложениях Express, вероятно, гораздо больше. Как мы видели, fastify-expressплагин позволяет нам продолжать использовать промежуточное ПО Express, если нам нужно. Это позволяет нам отложить переписывание нашего собственного промежуточного программного обеспечения Express в плагины Fastify. Но что мы можем сделать с заменой стороннего промежуточного программного обеспечения Express?

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

  • cors ➜ fastify-cors
  • helmet ➜ fastify-helmet
  • csurf ➜ fastify-csrf
  • express-session ➜ fastify-server-session
  • express-jwt ➜ fastify-jwt
  • http-errors ➜ fastify-sensible
  • serve-static ➜ fastify-static
  • multer ➜ fastify-multer

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

Вы можете найти полный список плагинов на странице Fastify Ecosystem.

Максимальное использование Fastify

Теперь, когда мы начали знакомиться с Fastify путем миграции приложения Express, самое время начать изучать другие функции Fastify, которые нам могут пригодиться.

Проверка

Fastify предоставляет функции для проверки запросов. Он использует Ajv (еще один валидатор схемы JSON) под капотом, что позволяет нам определять правила проверки с помощью схемы JSON.

Вот пример, который использует схему JSON для проверки тела запроса на POSTмаршруте:

const schema = {
  body: {
    type: "object",
    required: ["first_name"],
    properties: {
      first_name: { type: "string", minLength: 1 },
    },
  },
};

app.post("/user", { schema }, async (request, reply) => {
  reply.send(request.body);
});

Ошибки проверки автоматически форматируются и отправляются в виде ответа JSON:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "body should have required property 'first_name'"
}

Дополнительные сведения см. В документации по проверке и сериализации Fastify.

Логирование

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

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

{"level":30,"time":1615881822269,"pid":14323,"hostname":"localhost","msg":"Server listening at http://127.0.0.1:3000"}
{"level":30,"time":1615881829697,"pid":14323,"hostname":"localhost","reqId":"req-1","req":{"method":"GET","url":"/user/abc123","hostname":"localhost:3000","remoteAddress":"127.0.0.1","remotePort":38238},"msg":"incoming request"}
{"level":30,"time":1615881829704,"pid":14323,"hostname":"localhost","reqId":"req-1","res":{"statusCode":200},"responseTime":6.576989000663161,"msg":"request completed"}

Когда мы создаем экземпляр сервера Fastify, мы можем включить ведение журнала и настроить параметры, которые передаются pino. Затем Fastify автоматически выведет сообщения журнала, подобные показанным выше. Экземпляр регистратора доступен на экземпляре сервера Fastify (например, fastify.log.info(«…«)) и во всех объектах запроса (например, request.log.info(»…»)).

Обработка ошибок

Fastify предоставляет метод setErrorHandler (), который позволяет нам явно указать функцию для обработки ошибок. Это отличается от Express, где промежуточное ПО для обработки ошибок можно отличить только по параметрам, которые оно принимает ( err, req, res, next), и должно быть добавлено в определенном порядке.

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

Декораторы

Декораторы — это мощная функция в Fastify, которая позволяет нам настраивать основные объекты Fastify, такие как наш экземпляр сервера Fastify, а также объекты запроса и ответа. Вот пример базового декоратора:

fastify.register(async (fastify, options) => {

  fastify.decorate("yolo", () => {
    return { yo: "lo" };
  });

  fastify.get("/yolo", async function(request, reply) {
    // Our Fastify server instance is bound to `this`
    reply.send(this.yolo());
  });

});

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

Заключение

В этой статье мы узнали, как перенести существующее приложение Node.js из Express в Fastify. Мы увидели, как этот fastify-expressплагин может помочь нам постепенно переносить наши существующие приложения. Это позволяет нам начать пользоваться функциями, которые предоставляет Fastify, даже если в некоторых частях нашего приложения все еще используется Express.

Вот некоторые ресурсы, которые могут оказаться полезными при переходе от Express к Fastify:

  • Пример кода из этой статьи. Изучите код и запустите приложения, которые мы создали в этой статье.
  • Усовершенствовать документацию. Исчерпывающая документация по фреймворку Fastify.
  • Укрепить экосистему. Каталог плагинов для Fastify. Удобен для поиска плагинов для замены промежуточного программного обеспечения Express.
  • Модифицируйте пример приложения. Пример приложения, созданного одним из ведущих разработчиков Fastify. Он демонстрирует основные концепции, лучшие практики и рекомендации Fastify.
  • Усовершенствуйте сервер сообщества Discord. Отличное место для получения помощи и совета по разработке приложений с помощью Fastify.
Оцените статью
bestprogrammer.ru
Добавить комментарий