--- id: usage-examples title: Usage examples --- Each example builds one Fetch handler with `pipeline`. Entries run in array order on the request. The handler runs last and reads what the entries contributed to `ctx`. ### Gate a route behind CORS and a feature flag `withCors` runs first. It answers the CORS preflight (an `OPTIONS` request carrying `Access-Control-Request-Method`) with `204` before anything else runs. On the way out, it stamps `Access-Control-*` headers onto the response when the request's `Origin` is allowed. `withFeatureFlag` runs second. `evaluate` receives the request and decides: `true` admits it, `false` rejects it. It can be async, and it can return a verdict object instead of a boolean. A rejected request gets a `404` and never reaches the handler. An admitted request reaches the handler with `ctx.featureFlag` set. Both middleware ship in `@supabase/middleware`. The handler is plain Fetch, so the same stack runs on Node, Deno, Bun, and Cloudflare Workers; only the host entry point differs. ```ts import { pipeline } from '@supabase/middleware' import { withCors } from '@supabase/middleware/cors' import { withFeatureFlag } from '@supabase/middleware/feature-flag' export default { fetch: pipeline( [ withCors({ origin: ['https://app.example.com'], credentials: true }), withFeatureFlag({ name: 'beta-checkout', evaluate: (req) => req.headers.get('x-beta') === '1', }), ], async (_req, ctx) => Response.json({ feature: ctx.featureFlag.name }), ), } ``` ### Roll out an authenticated endpoint behind a flag Middleware from [`@supabase/server`](/docs/reference/server/introduction) drop into the same array. `withCors` runs first, so the preflight is answered before the auth gate. `withSupabase` runs second with `cors: 'disabled'`, because `withCors` owns CORS here. It verifies the caller's JWT and puts an RLS-scoped client on `ctx.supabase`. A request without valid credentials gets a `401` and never reaches the flag or the handler. The flag runs last. `evaluate` reads an environment variable through `getEnv`, so the endpoint returns `404` to every signed-in caller until `BETA_CHECKOUT` is set to `on`. Flip the variable to roll the endpoint out. Without `withCors`, `withSupabase` answers every `OPTIONS` request itself with `204` and wildcard CORS headers (`Access-Control-Allow-Origin: *`). That is enough when you do not need an origin allowlist. A layer that owns CORS must sit before `withSupabase` in the array. Placed after it, the preflight reaches the auth gate and gets a `401`. The entry form of `withSupabase` needs `@supabase/server` 1.6.0 or later. ```ts import { getEnv, pipeline } from '@supabase/middleware' import { withCors } from '@supabase/middleware/cors' import { withFeatureFlag } from '@supabase/middleware/feature-flag' import { withSupabase } from '@supabase/server' export default { fetch: pipeline( [ withCors({ origin: ['https://app.example.com'] }), withSupabase({ auth: 'user', cors: 'disabled' }), withFeatureFlag({ name: 'beta-checkout', evaluate: () => getEnv('BETA_CHECKOUT') === 'on', }), ], async (_req, ctx) => { const { data, error } = await ctx.supabase.from('carts').select() if (error) return Response.json({ error: 'query_failed' }, { status: 500 }) return Response.json(data) }, ), } ```