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>;

Де реєструвати

Дві області — глобально (кожен запит) та на маршрут:

// 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.