Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Thank you for your interest in contributing to `@supabase/server`! This document
- [Testing](#testing)
- [Submitting Changes](#submitting-changes)
- [Contributing a middleware](#contributing-a-middleware)
- [Contributing a framework adapter](#contributing-a-framework-adapter)
- [Framework adapters](#framework-adapters)
- [Release Process](#release-process)

## Getting Started
Expand Down Expand Up @@ -212,11 +212,11 @@ Middleware with no Supabase surface lives in its own package on top of [`@supaba

**Naming.** The `with` prefix means middleware: composable, chainable, and never the last entry in a chain. A terminal handler resolves the request instead of passing it on; it takes no prefix and lives outside `src/middleware/`. MCP-specific code stays separate from general middleware. When something turns out not to be MCP-specific, rename it and place it by the rules above.

## Contributing a framework adapter
## Framework adapters

Framework adapters (Hono, H3, …) are community-maintained and live in this repo under `src/adapters/`. They have **additional requirements** on top of the general PR guidelines above — tests covering every auth mode, no new runtime deps beyond a peer-dep, matching the existing adapter shape, and updating both adapter tables (in `README.md` and `src/adapters/README.md`).
The adapters under `src/adapters/` (Hono, H3, Elysia, NestJS) are deprecated and will be removed on December 1, 2026. New adapters are not accepted. Framework integration goes through the bridges in [`examples/frameworks`](examples/frameworks) and the [framework integration guide](https://supabase.com/docs/reference/server/frameworks).

See [`src/adapters/README.md`](src/adapters/README.md) for the full checklist before opening an adapter PR.
Bug fixes and framework-version bumps for the existing adapters are welcome until removal. See [`src/adapters/README.md`](src/adapters/README.md) for the maintenance rules.

## Release Process

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![pkg.pr.new](https://pkg.pr.new/badge/supabase/server)](https://pkg.pr.new/~/supabase/server)
[![Docs](https://img.shields.io/badge/docs-supabase.github.io-3ECF8E?logo=readthedocs&logoColor=white)](https://supabase.github.io/server/)

> **v1.X — Public Beta.** First stable release under SemVer: breaking changes only ship as a major bump. The package is still early — expect new adapters, ergonomic improvements, and features to land frequently in minor releases. Found a rough edge? [Open an issue](https://github.com/supabase/server/issues) or [submit a PR](https://github.com/supabase/server/blob/main/CONTRIBUTING.md).
> **v1.X — Public Beta.** First stable release under SemVer: breaking changes only ship as a major bump. The package is still early — expect ergonomic improvements and features to land frequently in minor releases. Found a rough edge? [Open an issue](https://github.com/supabase/server/issues) or [submit a PR](https://github.com/supabase/server/blob/main/CONTRIBUTING.md).

> **Coming from a `0.x` release?** See [MIGRATION.md](MIGRATION.md) for the v0 → v1 rename map (`allow` → `auth`, `'public'` → `'publishable'`, `authType` → `authMode`, `claims` → `jwtClaims`, …).

Expand Down Expand Up @@ -565,7 +565,7 @@ No. `@supabase/ssr` handles cookie-based session management for frameworks like
| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| How do I create a basic endpoint? | [`docs/getting-started.md`](docs/getting-started.md) |
| What auth modes are available? Array syntax? Named keys? | [`docs/auth-modes.md`](docs/auth-modes.md) |
| Which framework adapters exist? How do I contribute one? | [`src/adapters/README.md`](src/adapters/README.md) |
| Which framework adapters exist, and what replaces them? | [Framework Adapters](#framework-adapters) |
| How do I use this with Hono? | [`docs/adapters/hono.md`](docs/adapters/hono.md) |
| How do I use this with H3 / Nuxt? | [`docs/adapters/h3.md`](docs/adapters/h3.md) |
| How do I use this with Elysia? | [`docs/adapters/elysia.md`](docs/adapters/elysia.md) |
Expand Down
5 changes: 5 additions & 0 deletions docs/adapters/elysia.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Elysia Adapter

> [!WARNING]
> Adapters are deprecated and will be removed on December 1, 2026. We're no longer accepting new adapter submissions.
>
> New Elysia code uses the bridge in [`examples/frameworks/elysia`](https://github.com/supabase/server/tree/main/examples/frameworks/elysia). The [framework integration guide](https://supabase.com/docs/reference/server/frameworks) covers setup and the steps to move off the adapter.

## Setup

Install Elysia as a peer dependency:
Expand Down
5 changes: 5 additions & 0 deletions docs/adapters/h3.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# H3 / Nuxt Adapter

> [!WARNING]
> Adapters are deprecated and will be removed on December 1, 2026. We're no longer accepting new adapter submissions.
>
> New H3 and Nuxt code uses the bridge in [`examples/frameworks/h3`](https://github.com/supabase/server/tree/main/examples/frameworks/h3). The [framework integration guide](https://supabase.com/docs/reference/server/frameworks) covers setup and the steps to move off the adapter.

## Setup

Install H3 as a peer dependency:
Expand Down
5 changes: 5 additions & 0 deletions docs/adapters/hono.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Hono Adapter

> [!WARNING]
> Adapters are deprecated and will be removed on December 1, 2026. We're no longer accepting new adapter submissions.
>
> New Hono code uses the bridge in [`examples/frameworks/hono`](https://github.com/supabase/server/tree/main/examples/frameworks/hono). The [framework integration guide](https://supabase.com/docs/reference/server/frameworks) covers setup and the steps to move off the adapter.

## Setup

Install Hono as a peer dependency:
Expand Down
5 changes: 5 additions & 0 deletions docs/adapters/nestjs.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# NestJS Adapter

> [!WARNING]
> Adapters are deprecated and will be removed on December 1, 2026. We're no longer accepting new adapter submissions.
>
> New NestJS code uses the guard in [`examples/frameworks/nestjs`](https://github.com/supabase/server/tree/main/examples/frameworks/nestjs). The [framework integration guide](https://supabase.com/docs/reference/server/frameworks) covers setup and the steps to move off the adapter.

## Setup

Install NestJS as a peer dependency:
Expand Down
28 changes: 27 additions & 1 deletion scripts/smoke-pack.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
// allowances below is a hard dependency the package does not declare, and it
// breaks that consumer on import.
//
// Also checks that every doc path the shipped skill names exists in the
// installed package, since the skill points agents at node_modules, not at
// the repository.
//
// Run after `pnpm build`: `pnpm smoke:pack`.

import { execFileSync } from 'node:child_process'
Expand Down Expand Up @@ -113,6 +117,28 @@ try {
process.exit(1)
}

const installed = join(consumer, 'node_modules', pkg.name)
const skill = readFileSync(
join(installed, 'skills/supabase-server/SKILL.md'),
'utf8',
)
const docPaths = [
...new Set(
[...skill.matchAll(/`([A-Za-z0-9_./-]+\.md)`/g)].map((m) => m[1]),
),
]
const missingDocs = docPaths.filter((p) => !existsSync(join(installed, p)))
if (missingDocs.length > 0) {
failed = true
for (const p of missingDocs) {
console.error(`FAIL skill references ${p}, which is not in the package.`)
}
} else {
console.log(
`ok ${docPaths.length} doc paths named in the skill exist in the package`,
)
}

const loader = `
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
Expand Down Expand Up @@ -166,7 +192,7 @@ console.log(JSON.stringify(results))
}

if (failed) {
console.error('\nPacked entrypoints failed to load without optional peers.')
console.error('\nThe packed tarball failed the smoke checks above.')
process.exit(1)
}

Expand Down
75 changes: 54 additions & 21 deletions skills/supabase-server/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Use when planning or writing server-side code that uses `@supabase/

# @supabase/server

> **v1.0 — Public Beta.** First stable release under SemVer: breaking changes only ship as a major bump. The package is still early — expect new adapters, ergonomic improvements, and features to land frequently in minor releases. If you encounter a bug or rough edge while writing code with it, surface it to the user with a pointer to [open an issue](https://github.com/supabase/server/issues).
> **v1.0 — Public Beta.** First stable release under SemVer: breaking changes only ship as a major bump. The package is still early — expect ergonomic improvements and features to land frequently in minor releases. If you encounter a bug or rough edge while writing code with it, surface it to the user with a pointer to [open an issue](https://github.com/supabase/server/issues).

> **This is a brand new package.** There is no information available online yet — no blog posts, no Stack Overflow answers, no tutorials. Do not search the web for usage examples. Rely exclusively on the documentation files listed below and the source code in this repository.

Expand All @@ -30,15 +30,15 @@ Server-side utilities for Supabase. Handles auth, client creation, and context i
- Supports 4 auth modes: `user` (JWT), `publishable` (publishable key), `secret` (secret key), `none` (no credentials required)
- Array syntax (`auth: ['user', 'secret']`) is first-match-wins. A present-but-invalid JWT rejects with `InvalidJwtError` (`INVALID_JWT`) — it does not silently downgrade to the next mode.
- Provides composable core primitives for custom auth flows and framework integration
- Includes a Hono adapter for per-route auth
- Runs inside Hono, H3, Elysia, NestJS, and TanStack Start through `@supabase/middleware` bridges. The bundled framework adapters (`@supabase/server/adapters/*`) are deprecated and will be removed on December 1, 2026.

## Entry points

| Import | Deno / Edge Functions | Provides |
| ------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `@supabase/server` | `npm:@supabase/server@1` | `withSupabase`, `createSupabaseContext`, types, errors |
| `@supabase/server/core` | `npm:@supabase/server@1/core` | `verifyAuth`, `verifyCredentials`, `extractCredentials`, `resolveEnv`, `createContextClient`, `createAdminClient` |
| `@supabase/server/adapters/hono` | `npm:@supabase/server@1/adapters/hono` | `withSupabase` (Hono middleware variant) |
| `@supabase/server/adapters/hono` | `npm:@supabase/server@1/adapters/hono` | **Deprecated. Removed on December 1, 2026.** `withSupabase` (Hono middleware variant). New Hono code uses the bridge in the Hono quick start below. |
| `@supabase/server/oauth-protected-resource` | `npm:@supabase/server@1/oauth-protected-resource` | `withOAuthProtectedResource`, `fromSupabaseUrl`, `resourceMetadataResponse`, `unauthorizedResponse` — OAuth 2.1 discovery for MCP servers; see `docs/mcp.md` |

## Quick starts
Expand Down Expand Up @@ -106,29 +106,60 @@ export default {

### Hono

CORS is not handled by the adapter — use `hono/cors` middleware. See `docs/adapters/hono.md`.
> **The Hono adapter (`@supabase/server/adapters/hono`) is deprecated and will be removed on December 1, 2026.** New Hono code uses a bridge: one file you copy into your project. It runs `@supabase/middleware` entries inside Hono's middleware slot. The bridge needs `@supabase/server` 1.6.0 or later and Node 22 or later. The bridge file imports `@supabase/middleware`, so add it to your project as a direct dependency. `@supabase/server` depends on it, but a strict package manager such as pnpm does not let your code import a transitive dependency. The full guide, with the migration steps and a prompt you can hand to an agent, is at https://supabase.com/docs/reference/server/frameworks.

The bridge file is not in the npm package. Fetch it from the repository and keep it as is, comments included:

```bash
npm install @supabase/middleware
mkdir -p src/lib
curl --fail -o src/lib/supabase-middleware.ts \
https://raw.githubusercontent.com/supabase/server/main/examples/frameworks/hono/supabase-middleware.ts
```

```ts
// Node.js / Bun
import { Hono } from 'hono'
import { withSupabase } from '@supabase/server/adapters/hono'
import { withRequiredClaims } from '@supabase/server/middleware/required-claims'
import { withSupabaseClient } from '@supabase/server/middleware/client'

const app = new Hono()
app.use('*', withSupabase({ auth: 'user' }))
import { toHono } from './lib/supabase-middleware.js'

app.get('/todos', async (c) => {
const { supabase } = c.var.supabaseContext
const { data } = await supabase.from('todos').select()
return c.json(data)
})
// `withRequiredClaims` answers 401 to any request without a valid user JWT,
// so the routes below only run for signed-in callers.
const app = new Hono()
.use('*', toHono([withRequiredClaims(), withSupabaseClient()]))
.get('/todos', async (c) => {
const { data, error } = await c.var.supabase.from('todos').select()
if (error) return c.json({ error: error.message }, 500)
return c.json(data)
})
.get('/me', (c) => c.json({ id: c.var.jwtClaims.sub }))

export default app
export default { fetch: app.fetch }
```

Register the bridge with `.use()` in the same chain as the routes it gates. Hono applies middleware only to routes added after it. For browser callers, compose `withCors` from `@supabase/middleware/cors` ahead of the gate. On Deno and Supabase Edge Functions, import `hono`, `@supabase/middleware`, and `@supabase/server@1/middleware/*` with the `npm:` specifier.

**Pick the entries from the adapter's `auth` value.** The adapter rejected requests that failed its `auth` check. `withClaims()` does not. It contributes `jwtClaims: null` for an anonymous request and lets it through. Using it in place of `auth: 'user'` turns a 401 into a 200 with no error.

| Adapter config | Bridge entries |
| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `auth: 'user'`, or no config | `toHono([withRequiredClaims(), withSupabaseClient()])` |
| `auth: 'none'` | `toHono([withSupabaseClient()])` |
| `auth: 'publishable'` or `auth: 'secret'` | No bridge entry exists. That endpoint stays on `withSupabase({ auth: 'publishable' })` or `withSupabase({ auth: 'secret' })`. Do not hand-roll a key check. |

Never compose `withClaims()` and `withRequiredClaims()` in one array. Both contribute `jwtClaims`, and the bridge rejects the pair at compile time. Add `withSupabaseAdminClient()` from `@supabase/server/middleware/admin-client` only where the handler reads `supabaseAdmin`.

The entries contribute flat keys, so handlers read `c.var.supabase` and `c.var.jwtClaims`. No entry contributes `userClaims`. `jwtClaims` is the raw JWT payload: `userClaims.id` becomes `jwtClaims.sub`, `appMetadata` becomes `app_metadata`, and `userMetadata` becomes `user_metadata`.

<details>
<summary>Deprecated adapter (removed on December 1, 2026)</summary>

Existing code may still import the adapter. Recognize it, and migrate it with the table above. The adapter handles no CORS; it relies on `hono/cors`. See `docs/adapters/hono.md`.

```ts
// Deno / Supabase Edge Functions
import { Hono } from 'npm:hono'
import { withSupabase } from 'npm:@supabase/server@1/adapters/hono'
import { Hono } from 'hono'
import { withSupabase } from '@supabase/server/adapters/hono'

const app = new Hono()
app.use('*', withSupabase({ auth: 'user' }))
Expand All @@ -139,9 +170,11 @@ app.get('/todos', async (c) => {
return c.json(data)
})

export default { fetch: app.fetch }
export default app
```

</details>

### Cookie-based environments (compose with `@supabase/ssr`)

For Next.js / SvelteKit / Remix, **compose `@supabase/server` with [`@supabase/ssr`](https://github.com/supabase/ssr)** — they are not replacements for each other. `@supabase/ssr` owns cookies and refresh-token rotation (its middleware is required, otherwise the access token cookie goes stale and verification fails). In your Server Component or Route Handler, use `@supabase/ssr`'s `createServerClient` to read the (middleware-refreshed) session, hand the access token to `verifyCredentials` from `@supabase/server/core`, then build the typed clients with `createContextClient` + `createAdminClient`. See `docs/ssr-frameworks.md` for the full adapter pattern.
Expand Down Expand Up @@ -436,9 +469,9 @@ The full documentation lives in the `docs/` directory of the `@supabase/server`
| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| How do I create a basic endpoint? | `docs/getting-started.md` |
| What auth modes are available? Array syntax? Named keys? | `docs/auth-modes.md` |
| Which framework adapters exist? How do I contribute one? | `src/adapters/README.md` |
| How do I use this with Hono? | `docs/adapters/hono.md` |
| How do I use this with H3 / Nuxt? | `docs/adapters/h3.md` |
| Which framework adapters exist, and what replaces them? | `README.md`, section "Framework Adapters" |
| How do I use the deprecated Hono adapter? | `docs/adapters/hono.md` |
| How do I use the deprecated H3 / Nuxt adapter? | `docs/adapters/h3.md` |
| How do I run middleware entries inside Hono, H3, Elysia, NestJS, or TanStack Start without an adapter? | https://supabase.com/docs/reference/server/frameworks |
| How do I use low-level primitives for custom flows? | `docs/core-primitives.md` |
| How do environment variables work across runtimes? | `docs/environment-variables.md` |
Expand Down
Loading
Loading