Middleware
Хуки before / after / wrap, що компонуються на сервері hopak().
Три хуки для конвеєра запитів. Типізовані функції, а не Koa-подібний ланцюг (ctx, next) — немає next(), який можна забути викликати.
type Before = (ctx: RequestContext) =>
Promise<Response | void> | Response | void;
type After = (
ctx: RequestContext,
result: { response?: Response; error?: unknown },
) => Promise<void> | void;
type Wrap = (
ctx: RequestContext,
run: () => Promise<Response>,
) => Promise<Response>;
Before— виконується перед хендлером. КиньтеHopakError, щоб перервати виконання з цим статусом. ПовернітьResponse, щоб перервати цією відповіддю. Поверніть нічого, щоб продовжити. Мутаціїctxпроходять далі (наприклад,ctx.user = ...). Правильне місце для авторизації, rate-limiting, request-id.After— виконується після хендлера (або помилки) з фінальною відповіддю. Не може змінити відповідь — тільки читання. Використовуйте для access-логів, метрик, аудиту. Якщо кидає помилку, помилка логується, а запит усе одно завершується.Wrap— обгортає виконання хендлера (плюсbeforeрівня маршруту).run()виробляє відповідь. Використовуйте тільки коли спостереження недостатньо — транзакції на запит, кеші в межах запиту, поширення correlation-id черезasync_hooks.
Де реєструвати
Дві області — глобально (кожен запит) та на маршрут:
// main.ts — global
import { hopak, requestId, requestLog } from '@hopak/core';
await hopak()
.before(requestId())
.after(requestLog())
.wrap(async (_ctx, run) => run()) // rarely needed
.listen();
// app/routes/api/posts.ts — per-route
export const POST = defineRoute({
before: [requireAuth()],
after: [audit],
handler: async (ctx) => { /* … */ },
});
Хелпери crud.* приймають ті ж опції другим аргументом — див.
CRUD → Gate a CRUD verb.
Порядок виконання
Для одного запиту:
global.before[] → wrap[] → route.before[] → handler
(throw or return Response short-circuits)
route.after[] → global.after[]
Wrap вкладаються — зовнішній виконується першим на вході й останнім
на виході (як шари цибулі).
hopak().before/.after/.wrap заморожується після listen()
Реєстрація middleware після старту сервера кидає помилку:
const app = hopak();
await app.listen();
app.before(requestId());
// Error: hopak().before(): cannot register middleware
// after listen() — add it before starting the server.
Це захищає від частково застосованих middleware на живих запитах.
Вбудовані: requestId() + requestLog()
Див. Recipe 23 для повного огляду. Одна команда вмикає обидва:
hopak use request-log.
EMPTY_MIDDLEWARE
Експортований sentinel для { before: [], after: [], wrap: [] }.
Корисно, якщо ви компонуєте власний об’єкт Middleware та хочете мати
явний порожній default.