Const Alphaimport { pipeline } from '@supabase/middleware'
import { withRequiredClaims } from '@supabase/server/middleware/required-claims'
import { withPostgresClient } from '@supabase/server/middleware/postgres'
export default {
fetch: pipeline([withRequiredClaims(), withPostgresClient()], async (req, ctx) => {
const rows = await ctx.postgres.query`select id, title from posts`
return Response.json({ rows, caller: ctx.jwtClaims.sub })
}),
}
The composable middleware surface tracks @supabase/middleware 0.x — entry
shapes, context keys, and config options may change between 0.x releases.
Alpha. The user-mode auth gate: requires a valid user JWT and contributes non-null
ctx.jwtClaims. Verification runs against the project JWKS, the same corewithSupabaseuses for itsuserauth mode.This is the required-caller counterpart to
withClaims, which contributes claims when a token is present and lets token-less requests proceed as anonymous. A pipeline picks one or the other, "claims required" or "claims if present"; composing both is a compile-time conflict on thejwtClaimskey.Behavior — every short-circuit uses the standard error payload, with the same code
withSupabase({ auth: 'user' })returns for an identical request:Authorization: Bearertoken → 401MISSING_CREDENTIALS. The handler never runs.sb_*API key in that position → 401UNUSABLE_CREDENTIAL: a credential arrived, just not a user JWT.INVALID_JWT, naming the specific reason.JWKS_NOT_CONFIGURED; verification is not optional and there is no decode-only mode.Because the contribution is non-null, gated handlers read
ctx.jwtClaimsdirectly, with no?.sub ?? 'anon'fallbacks. Downstream entries declaring ajwtClaimsprerequisite, such aswithPostgresClient, compose with no further verification.The 401 and 500 short-circuits carry no CORS headers, and a bare pipeline answers no
OPTIONSpreflight. For browser callers, composewithCors(@supabase/middleware/cors) ahead of the gate: it answers preflight before the gate runs and stampsAccess-Control-*headers on the short-circuit responses.After
withSupabasein a pipeline the context already carries verifiedjwtClaims, so this gate is unnecessary there and placing it afterwithSupabaseis a compile-time conflict. UsewithSupabase({ auth: 'user' })to gate that path.