docs: add usage examples partial to the server reference (#50858)

## Problem

The `@supabase/server` reference goes straight from Installing to the
generated API reference. It has no worked example. The middleware
reference has a "Usage examples" page in that spot (#50461).

## Solution

A new "Usage examples" partial sits between Installing and the generated
reference. It has three examples, taken from the server repo's
`docs/getting-started.md`:

- **Protect an endpoint with a user JWT:** `withSupabase({ auth: 'user'
})`, its CORS handling, and `ctx.supabase` vs `ctx.supabaseAdmin`.
- **Serve a public endpoint:** `auth: 'none'`, plus the `verify_jwt =
false` setting Edge Functions need.
- **Build the context yourself:** `createSupabaseContext` returning `{
data, error }`.

Files:

- `spec/reference/server/v1/partials/usage-examples.mdx` and its
`docs/ref/server/` mirror
- `usage-examples` added to `partialsOrder` in
`spec/reference/server/v1/config.json`

Every claim is checked against the source code in `supabase/server`,
`supabase/middleware`, and `supabase/cli`.

## Preview links

| Site | Preview |
| ---- | ------- |
| Docs |
[/docs/reference/server/usage-examples](https://docs-git-docs-server-usage-examples-supabase.vercel.app/docs/reference/server/usage-examples)
|

## Review instructions

1. Open the preview link.
2. Check that "Usage examples" appears in the sidebar between Installing
and the generated reference.
3. Check that the three examples render with the code on the right.

## Checklist

- [ ] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [x] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which references
[WORD_LIST](https://github.com/supabase/supabase/blob/master/apps/docs/WORD_LIST.md)
and the docs
[CONTRIBUTING](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md)
guide



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added server usage examples for protecting endpoints, serving public
endpoints, and creating Supabase context manually.
* Documented runtime requirements, JWT verification settings, CORS
behavior, and the differences between caller-scoped and admin access.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Katerina Skroumpelou authored and GitHub committed 2026-09-28 16:06:58 +03:00
1 parent 3d8da2d827
commit db63b4d6a2
3 files changed
+197 -1

No files matched your search

@@ -0,0 +1,98 @@
---
id: usage-examples
title: Usage examples
---
Each example is a Fetch handler. Deno, Bun, and Supabase Edge Functions run the `export default { fetch }` shape as is. Cloudflare Workers also need the `nodejs_compat` flag or the `env` option, so `withSupabase` can read your project's URL and keys. On Node, use an adapter or the primitives with your framework.
### Protect an endpoint with a user JWT
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`withSupabase` wraps your handler. With `auth: 'user'`, it checks the caller's JWT before your handler runs. A request without valid credentials gets a JSON error response, and your handler never runs.
`withSupabase` also answers every `OPTIONS` request with `204` and adds CORS headers to every response. Set `cors: 'disabled'` when your framework handles CORS.
`ctx.supabase` is scoped to the caller, so Row Level Security policies apply. `ctx.supabaseAdmin` bypasses RLS. Use it only for work that needs full database access.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
}),
}
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
### Serve a public endpoint
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`auth: 'none'` lets every request through. `ctx.userClaims` is `null`, and `ctx.supabase` runs as an anonymous client.
Supabase Edge Functions check for a valid JWT on every request by default. For a function that uses `auth: 'none'`, `auth: 'publishable'`, or `auth: 'secret'`, turn that check off in `supabase/config.toml`. The `auth` setting on `withSupabase` then decides who gets in.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'none' }, async () => {
return Response.json({ status: 'ok', time: new Date().toISOString() })
}),
}
```
```toml supabase/config.toml
[functions.my-function]
verify_jwt = false
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
### Build the context yourself
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`createSupabaseContext` runs the same auth checks as `withSupabase` but returns `{ data, error }` instead of a response. Use it inside a framework route handler, or anywhere you want to shape the error response yourself.
On success, `data` is the same `SupabaseContext` that `withSupabase` passes to your handler. On failure, `error.status` holds the HTTP status to send. `createSupabaseContext` does not handle CORS.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { createSupabaseContext } from '@supabase/server'
export default {
fetch: async (req: Request) => {
const { data: ctx, error } = await createSupabaseContext(req, { auth: 'user' })
if (error) {
return Response.json(error.toJSON(), { status: error.status })
}
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
},
}
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
@@ -1,5 +1,5 @@
{
"categoryOrder": ["Middleware", "Primitives", "Adapters", "Errors", "Types"],
"partialsOrder": ["introduction", "installing"],
"partialsOrder": ["introduction", "installing", "usage-examples"],
"navigationPrefixes": {}
}
@@ -0,0 +1,98 @@
---
id: usage-examples
title: Usage examples
---
Each example is a Fetch handler. Deno, Bun, and Supabase Edge Functions run the `export default { fetch }` shape as is. Cloudflare Workers also need the `nodejs_compat` flag or the `env` option, so `withSupabase` can read your project's URL and keys. On Node, use an adapter or the primitives with your framework.
### Protect an endpoint with a user JWT
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`withSupabase` wraps your handler. With `auth: 'user'`, it checks the caller's JWT before your handler runs. A request without valid credentials gets a JSON error response, and your handler never runs.
`withSupabase` also answers every `OPTIONS` request with `204` and adds CORS headers to every response. Set `cors: 'disabled'` when your framework handles CORS.
`ctx.supabase` is scoped to the caller, so Row Level Security policies apply. `ctx.supabaseAdmin` bypasses RLS. Use it only for work that needs full database access.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
}),
}
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
### Serve a public endpoint
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`auth: 'none'` lets every request through. `ctx.userClaims` is `null`, and `ctx.supabase` runs as an anonymous client.
Supabase Edge Functions check for a valid JWT on every request by default. For a function that uses `auth: 'none'`, `auth: 'publishable'`, or `auth: 'secret'`, turn that check off in `supabase/config.toml`. The `auth` setting on `withSupabase` then decides who gets in.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'none' }, async () => {
return Response.json({ status: 'ok', time: new Date().toISOString() })
}),
}
```
```toml supabase/config.toml
[functions.my-function]
verify_jwt = false
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>
### Build the context yourself
<RefSubLayout.EducationRow>
<RefSubLayout.Details>
`createSupabaseContext` runs the same auth checks as `withSupabase` but returns `{ data, error }` instead of a response. Use it inside a framework route handler, or anywhere you want to shape the error response yourself.
On success, `data` is the same `SupabaseContext` that `withSupabase` passes to your handler. On failure, `error.status` holds the HTTP status to send. `createSupabaseContext` does not handle CORS.
</RefSubLayout.Details>
<RefSubLayout.Examples>
```ts
import { createSupabaseContext } from '@supabase/server'
export default {
fetch: async (req: Request) => {
const { data: ctx, error } = await createSupabaseContext(req, { auth: 'user' })
if (error) {
return Response.json(error.toJSON(), { status: error.status })
}
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
},
}
```
</RefSubLayout.Examples>
</RefSubLayout.EducationRow>