@supabase/middleware - v0.2.0
    Preparing search index...

    Function defineMiddleware

    • Defines a middleware.

      A middleware runs against an inbound Request and the upstream context. It either short-circuits by returning a Response, or contributes a typed value at ctx[key] by returning a single-key object { [key]: contribution } — the framework picks result[key], merges it into the context, and calls the inner handler. Extra keys on the returned object are ignored at runtime. Note they are not caught at compile time: run's return is contextually typed against a Response | { [key]: … } union via a generic mapped type, a position where TypeScript suppresses excess-property checks — so a stray sibling key slips past the types (harmlessly). The runtime contributionOf guard is the backstop, and it throws only when the key is missing entirely (e.g. a computed/typo'd key the types couldn't see). To opt into the excess check on a given middleware, annotate run's inner return type explicitly.

      run is request-side by default (the common case): it runs before the handler and never observes the handler's Response. Response-shaped concerns (CORS, envelopes) normally belong in the handler or a .then() on the entry.

      Response seam (escape hatch). When a middleware genuinely needs to see the way out — stamp headers, time the request, run finally cleanup — write run as an async function* instead of async. yield is the seam: code before it is the request phase; you yield the contribution and the yield expression resolves to the downstream Response for the response phase. yield always means "run downstream and hand me the response" — short-circuit with a plain return new Response(...), exactly as the request-side path does, and yield at most once. This is the one place a middleware observes the handler's response, and writing function* is the visible, opt-in signal.

      withFoo(config, handler) produces a single (req, ctx) => Response function. Middleware nest directly, and the outermost is used as the runtime's fetch handler with no wrapperexport default { fetch: withFoo(config, handler) }. When the host invokes it, the second argument is a platform value (a Workers env, a Deno ServeHandlerInfo), not an upstream context; the wrapper detects this via isContext and seeds a fresh context instead of merging it, so platform arguments never leak into ctx — they are only captured as the module-scoped platform env behind the importable getEnv.

      Typing:

      • Prerequisite-free middleware are entry-able. Their produced handler has an optional ctx, so it satisfies a bare (req) => Response fetch entry and self-seeds a fresh context.
      • Middleware with In prerequisites require ctx. They can only be nested inside a wrapper that supplies those keys — never a bare entry — which keeps the prerequisite from being a type-lie at the top level.
      • Collision detection. Composing where the upstream already has the key resolves Base to a Conflict<Key> sentinel; the stack fails to typecheck.
      • Accumulation. Cross-middleware dependencies declared via In type with no ceremony. For the innermost handler to ambiently see every upstream key, annotate the outermost with satisfies FetchHandler (a type-only anchor).

      Type Parameters

      • const Key extends string

        The literal-string key contributed to ctx.

      • Config

        Configuration object the middleware accepts.

      • In extends object = Record<never, never>

        Upstream prerequisites besides BaseContext. Defaults to none.

      • Contribution = unknown

        Shape of the value placed at ctx[Key].

      Parameters

      • spec: {
            key: Key;
            run: (
                config: Config,
            ) => (
                req: Request,
                ctx: In,
            ) =>
                | Promise<Response | { [K in string]: Contribution }>
                | AsyncGenerator<
                    Response
                    | { [K in string]: Contribution },
                    void | Response,
                    Response,
                >;
        }

      Returns Middleware<Key, Config, In, Contribution>

      import { defineMiddleware } from '@supabase/middleware'

      export const withFeatureFlag = defineMiddleware<
      'featureFlag',
      { name: string; evaluate: (req: Request) => boolean },
      {},
      { name: string; enabled: true }
      >({
      key: 'featureFlag',
      run: (config) => async (req) => {
      if (!config.evaluate(req)) {
      return Response.json({ error: 'feature_disabled' }, { status: 404 })
      }
      return { featureFlag: { name: config.name, enabled: true } }
      },
      })