Skip to content

Search docs

Jump between documentation pages.

Browse docs

API reference: Middleware & helpers

Built-in middleware, the every/some/except composition primitives, typed dependencies, config validation, structured logging, the startup banner, and connection-info helpers. Everything on this page is exported from the root @daloyjs/core barrel. See the API reference overview for the full module map.

A typical middleware stack
Platform hygieneapplied by secureDefaults
requestId()secureHeaders()
Traffic shaping
rateLimit()loadShedding()compression()
Authentication & access
bearerAuth()jwk()requireScopes()csrf()
Route handlertyped ctx, typed response
Hooks compose top to bottom. every(), some(), and except() combine layers; defineDependency() injects per-request values into ctx.state.

Built-in middleware

ts
requestId(opts?: RequestIdOptions): Hooks
secureHeaders(opts?: SecureHeadersOptions): Hooks
cors(opts: CorsOptions): Hooks
rateLimit(opts: RateLimitOptions): Hooks
loginThrottle(opts?: LoginThrottleOptions): Hooks
timing(headerName?: string): Hooks
compression(opts?: CompressionOptions): Hooks
bearerAuth(opts: BearerAuthOptions): Hooks
basicAuth(opts: BasicAuthOptions): Hooks
markAuthHook(hooks: Hooks): Hooks
const AUTH_HOOK_MARKER: unique symbol  // stamped by built-ins and markAuthHook()
csrf(opts?: CsrfOptions): Hooks
fetchMetadata(opts?: FetchMetadataOptions): Hooks   // Sec-Fetch-Site/Mode/Dest enforcement
requireScopes(scopes: string | string[]
            | { all?: string[]; any?: string[] }): Hooks
ipRestriction(opts: IpRestrictionOptions): Hooks    // CIDR allow/deny
loadShedding(opts?: LoadSheddingOptions): Hooks
etag(opts?: ETagOptions): Hooks                      // 304 + Set-Cookie / Cache-Control skip

interface RateLimitOptions {
  windowMs: number;
  max: number;
  keyGenerator?: (ctx: RateLimitContext) => string; // may run on an early auth rejection
  store?: RateLimitStore;          // default in-memory; use redisRateLimitStore for clusters
  trustProxyHeaders?: boolean;
  retryAfter?: boolean;
  groupId?: string;
}

interface BearerAuthOptions {
  validate: (token: string) => boolean | Promise<boolean>;  // static check; token only
  verify?: BearerAuthVerifyHook;    // (token, ctx) => boolean | void; per-request revalidation
  realm?: string;
}

Composition primitives

ts
every(...layers: Hooks[]): Hooks      // run every lifecycle phase in order
some (...layers: Hooks[]): Hooks      // pass the first successful preBody/beforeHandle auth gate
except(when: ExceptPredicate, hooks: Hooks): Hooks  // exempt paths from preBody + beforeHandle gates

type ExceptPredicate =
  | string                            // path glob: "*" = one segment, "**" = any suffix
  | string[]                          // any-of globs
  | ((ctx) => boolean | Promise<boolean>);

Dependencies (typed DI chain)

ts
defineDependency<TName, TValue, TStateKey>(opts: {
  name: TName;
  dependsOn?: readonly string[];      // refuses cycles at registration
  stateKey?: TStateKey;
  resolve: (ctx) => TValue | Promise<TValue>;
}): DependencyHooks   // per-request cached; runs once per dependency per request

Configuration

ts
defineConfig<S extends StandardSchemaV1>(opts: {
  schema: S;
  source?: ConfigSource;               // default: "env" (process.env)
  stderr?: { write(chunk: string): void } | false;
}): Promise<StandardSchemaV1.InferOutput<S>>;
  // Async. Validates once at startup; throws ConfigValidationError on missing/invalid values.

type ConfigSource =
  | "env"
  | { kind: "env";    env: Record<string, string | undefined> }
  | { kind: "file";   path: string; parse?: (text: string) => unknown }
  | { kind: "object"; data: Record<string, unknown> }
  | { kind: "custom"; resolve: () => Promise<Record<string, unknown>> };

class ConfigValidationError extends Error {
  readonly issues: ReadonlyArray<{ key: string; message: string }>;
}

Logging

ts
type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";

createLogger(opts?: ConsoleLoggerOptions): Logger;
const noopLogger: Logger;
const DEFAULT_REDACT_KEYS: ReadonlyArray<string>;  // password, token, secret, authorization, ...

interface ConsoleLoggerOptions {
  level?: LogLevel;
  bindings?: Record<string, unknown>;
  write?: (line: string) => void;
  redact?: LoggerRedactionOptions;     // { keys?, replacer? }
}

interface Logger {
  trace(obj?, msg?): void;
  debug(obj?, msg?): void;
  info (obj?, msg?): void;
  warn (obj?, msg?): void;
  error(obj?, msg?): void;
  fatal(obj?, msg?): void;
  child(bindings: Record<string, unknown>): Logger;
}

Startup banner

ts
interface StartupBannerLink { label: string; url: string }
interface StartupBannerOptions {
  name?: string;        // default: "DaloyJS"
  version?: string;
  url: string;
  runtime?: string;     // e.g. "Node.js", "Bun"
  links?: StartupBannerLink[];
  color?: boolean;
  ascii?: boolean;
}

formatStartupBanner(opts: StartupBannerOptions): string;
printStartupBanner(opts: StartupBannerOptions): void;

Connection info & proxy posture

ts
type BehindProxyConfig = "none" | "loopback" | { hops: number } | { cidrs: readonly string[] };
interface ConnInfo { remoteAddress?: string; remotePort?: number; tls?: boolean }

getConnInfo(req: Request): ConnInfo | undefined;
setConnInfo(req: Request, info: ConnInfo): void;   // adapter helper
assertBehindProxy(cfg: BehindProxyConfig | undefined): void;
resolveClientIp(ctx, cfg?: BehindProxyConfig): string | undefined;
readRemoteAddress(ctx): string | undefined;
readRemotePort(ctx): number | undefined;
pickForwardedForByHops(header: string, hops: number): string | undefined;

Subdomains (Public-Suffix-aware)

ts
subdomains(hostname: string, opts?: SubdomainsOptions): SubdomainsResult;

interface SubdomainsResult {
  subdomain: string | undefined;       // e.g. "api" for "api.example.co.uk"
  registrableDomain: string | undefined;
  publicSuffix: string | undefined;
}

const PSL_SNAPSHOT_DATE: string;       // ISO date of the bundled PSL snapshot
const MAX_SNAPSHOT_AGE_DAYS: number;   // refuses to use a stale snapshot
const PSL_PUBLIC_SUFFIXES: ReadonlySet<string>;

Next up: security & auth helpers.