Зарегистрируйтесь, чтобы продолжить обучение

Middlewares JS: Веб-разработка

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

Оборачивание

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

Представим, что у нас есть функция, которая прибавляет 5 к аргументу:

const plusFive = x => x + 5

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

const doublePlusFive = x => plusFive(x * 2)

Точно так же мы можем расширить и вторую функцию:

const nextFunc = x => doublePlusFive(x - 10) - 3

nextFunc(20) // ((((20 - 10) * 2) + 5) - 3) = 22

В общем случае расширение функции выглядит так:

const nextF = (/* args */) => {
  // preprocessing
  const result = prevF(/* updatedArgs */)
  // afterprocessing
  return /* newResult */
}

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

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

Чтобы мидлвары заработали, к приложению нужно подключить плагин @fastify/middie или @fastify/express с помощью метода register().

Установка плагина:

npm i @fastify/middie

Пример кода с подключением:

import fastify from 'fastify'
import middie from '@fastify/middie'
import morgan from 'morgan'

const app = fastify()
const port = 3000

await app.register(middie)

app.use((req, res) => {
  res.end('Hello from middleware!')
})

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

В примере выше подключена мидлвара, которая возвращает текст 'Hello from middleware!'. Далее мы разберем более подробно как это работает.

Middleware

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

pipline

В свою очередь каждая мидлвара принимает на вход три параметра: request, response и next. Она может поменять их и в конце должна вызвать next для передачи управления следующей по списку мидлваре. В этом и заключается вся мощь микрофреймворков. Удачный дизайн позволяет легко разбивать систему на модули-мидлвары и расширять за счёт мидлвар, которые, в большом количестве, пишут сторонние разработчики.

app.use((req, res, next) => {
  res.setHeader('key', 'value')
  next()
})

В примере выше мидлвара добавляет заголовок в каждый запрос.

Mount middleware

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

app.use('/foo', (req, res, next) => {
  // req.url starts with "/foo"
  next()
})

app.use('/bar', (req, res, next) => {
  // req.url starts with "/bar"
  next()
})

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

Terminate

Но далеко не всегда мы хотим двигаться вглубь. Более того, в какой-то момент одна из мидлвар должна взять обработку на себя.

app.use((req, res) => {
  res.end('Hello from first!')
})

app.use((req, res) => {
  res.send('Hello from second!')
})

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

Библиотеки

Многие библиотеки, добавляющие функционал в приложение, работают с помощью мидлвар. Разберем пример на библиотеке morgan. Это библиотека для логирования запросов. Перед ее использованием установим пакет:

npm i morgan

Пример кода:

import fastify from 'fastify'
import middie from '@fastify/middie'
import morgan from 'morgan'

const app = fastify()
const port = 3000
const logger = morgan('combined')

await app.register(middie)
app.use(logger)

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

Выше в примере мы создали функцию логирования с помощью функции morgan() и затем передали его в качестве мидлвары. Теперь на каждый запрос будет появляться подробный лог в приложении. Например:

Example app listening on port 3000
127.0.0.1 - - [18/Apr/2024:08:15:59 +0000] "GET / HTTP/1.1" 404 72 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0"

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff