Search docs

Jump between documentation pages.

Browse docs

Middleware combinators

Think of it like… assembling a security checkpoint. every() chains the metal detector, the bag scan, and the ID check in order. some()says "any valid ID gets you in — passport OR driver's license OR staff badge." except() waves the staff entrance through without the queue.

DaloyJS exposes three small composition primitives for Hooksbundles. They let you package curated middleware stacks as a single value, express "any of these proofs is enough" authentication, and exempt specific paths from a check — all without any runtime dependencies.

every() — run a whole stack in order

every(...layers) merges several Hooks bundles into one that runs each layer in registration order. It is equivalent to calling app.use(...) for each bundle, but lets you name and reuse a curated stack. All lifecycle phases compose, and symbol-keyed security markers (CORS / CSRF / session / secure-headers) are forwarded so boot-time guards still see them.

ts
import { App, every, requestId, bearerAuth, rateLimit } from "@daloyjs/core";

const app = new App();

// Package "the admin stack" as a single reusable value.
const adminStack = every(
  requestId(),
  bearerAuth({ validate: (token) => token === process.env.ADMIN_TOKEN }),
  rateLimit({ windowMs: 60_000, max: 30, groupId: "admin" }),
);

app.use(adminStack);

some() — accept any one proof of identity

some(...layers) runs each bundle's beforeHandlein order and accepts the request as soon as one of them passes without throwing. Use it for "this route accepts a bearer token OR a signed session cookie OR an API key" patterns.

ts
import { App, some, bearerAuth, session } from "@daloyjs/core";

const app = new App();

app.use(some(
  bearerAuth({ validate: (token) => token === process.env.PUBLIC_API_TOKEN }),
  session(),
));

Semantics worth knowing:

  • The first bundle that resolves without throwing wins; its context mutations (headers, ctx.state) are preserved.
  • A bundle that returns a Response is treated as a denial, and the next bundle gets a chance. If every bundle denies, the first denial wins.
  • When the first denial is a thrown error, that error is rethrown — so place the auth method whose WWW-Authenticate challenge you want clients to see first.
  • Only the beforeHandle evaluation strategy changes; all other phases still compose normally.

except() — apply everywhere but a few paths

except(when, hooks) runs a bundle on every request except those matching when. The canonical use is "apply auth everywhere except the public endpoints."

ts
import { App, except, bearerAuth } from "@daloyjs/core";

const app = new App();

app.use(except(
  ["/health", "/openapi.json", "/docs/**"],
  bearerAuth({ validate: (token) => token === process.env.API_TOKEN }),
));

The when matcher accepts:

  • A path string starting with /. * matches one path segment (no slash); ** matches any suffix (zero or more segments).
  • An array of such path patterns.
  • A predicate function that receives the request context and returns true to skip the gated bundle.

Only the beforeHandle phase is gated. The surrounding onRequest/afterHandle/onSend/onResponse phases still run, so shared concerns like request-id propagation are never accidentally exempted.

Composing the three

The primitives nest. A common production shape is "run the full security stack everywhere except the health and docs routes":

ts
import { App, every, except, requestId, secureHeaders, bearerAuth } from "@daloyjs/core";

const app = new App();

const protectedStack = every(
  requestId(),
  secureHeaders(),
  bearerAuth({ validate: (token) => token === process.env.API_TOKEN }),
);

app.use(except(["/health", "/openapi.json"], protectedStack));