Conversation
…docs with npm SKILL.md now instructs agents to find documentation in the installed @supabase/server package (node_modules or repo root) instead of using relative paths. Added docs/ and SKILL.md to package.json files array so they ship with npm installs.
There was a problem hiding this comment.
Pull request overview
Adds a packaged documentation set for @supabase/server (plus a SKILL.md entry point) so agents/users can discover and navigate SDK usage directly from the installed npm package.
Changes:
- Add
SKILL.mdas a doc router for common questions and entry points. - Add
docs/with focused guides covering auth, adapters, primitives, env, errors, webhooks, generics, and a full API reference. - Ensure
docs/andSKILL.mdship in the npm tarball viapackage.json#files.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Include docs/ and SKILL.md in published npm package files. |
| SKILL.md | Provide a lightweight doc entry point mapping questions to specific docs. |
| docs/getting-started.md | Quickstart and createSupabaseContext usage examples. |
| docs/auth-modes.md | Explain allow modes, syntax, and request requirements. |
| docs/hono-adapter.md | Document Hono middleware adapter and patterns (CORS, error handling, overrides). |
| docs/core-primitives.md | Document low-level primitives pipeline and examples. |
| docs/environment-variables.md | Document env var formats, runtime behavior, and overrides. |
| docs/error-handling.md | Document error classes, surfacing patterns, and handling examples. |
| docs/typescript-generics.md | Document Database generics across entrypoints and adapters. |
| docs/webhooks.md | Document verifyWebhookSignature usage and security notes. |
| docs/api-reference.md | Provide a consolidated export-by-entrypoint reference. |
| CHANGELOG.md | Reformat existing changelog bullet formatting. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| console.log(ctx.userClaims.id) // "d0f1a2b3-..." | ||
| console.log(ctx.userClaims.email) // "user@example.com" | ||
| console.log(ctx.userClaims.role) // "authenticated" | ||
|
|
||
| // ctx.claims has the raw JWT payload | ||
| console.log(ctx.claims.sub) // same as userClaims.id | ||
| console.log(ctx.claims.exp) // token expiration (epoch seconds) |
There was a problem hiding this comment.
In this 'user' mode example, SupabaseContext.userClaims and claims are typed as nullable (UserClaims | null, JWTClaims | null) even though they’re guaranteed at runtime for allow: 'user'. As written, ctx.userClaims.id / ctx.claims.sub won’t type-check under strictNullChecks; use non-null assertions (ctx.userClaims!.id, ctx.claims!.sub) or add an explicit guard before accessing these fields.
| console.log(ctx.userClaims.id) // "d0f1a2b3-..." | |
| console.log(ctx.userClaims.email) // "user@example.com" | |
| console.log(ctx.userClaims.role) // "authenticated" | |
| // ctx.claims has the raw JWT payload | |
| console.log(ctx.claims.sub) // same as userClaims.id | |
| console.log(ctx.claims.exp) // token expiration (epoch seconds) | |
| console.log(ctx.userClaims!.id) // "d0f1a2b3-..." | |
| console.log(ctx.userClaims!.email) // "user@example.com" | |
| console.log(ctx.userClaims!.role) // "authenticated" | |
| // ctx.claims has the raw JWT payload | |
| console.log(ctx.claims!.sub) // same as userClaims.id | |
| console.log(ctx.claims!.exp) // token expiration (epoch seconds) |
| import { EnvError } from '@supabase/server' | ||
|
|
||
| try { | ||
| const supabase = createContextClient({ auth: { token: auth.token } }) |
There was a problem hiding this comment.
In the “Client factories throw” example, auth.token is referenced but auth isn’t defined in this code block (it’s in a different snippet above). To keep this block self-contained, either include the verifyAuth(...) call in the same snippet or replace auth.token with a placeholder variable (e.g., token) that’s defined locally.
| const supabase = createContextClient({ auth: { token: auth.token } }) | |
| const token = 'user-jwt-token' | |
| const supabase = createContextClient({ auth: { token } }) |
| const supabase = createContextClient<Database>({ | ||
| auth: { token: auth.token }, |
There was a problem hiding this comment.
This core-primitives example references auth.token, but auth isn’t defined in the snippet. To make it copy/paste-able, either show where auth comes from (e.g., const { data: auth } = await verifyAuth(...)) or replace it with a locally-defined token placeholder.
| const supabase = createContextClient<Database>({ | |
| auth: { token: auth.token }, | |
| const token = 'YOUR_ACCESS_TOKEN' | |
| const supabase = createContextClient<Database>({ | |
| auth: { token }, |
| import { createContextClient } from '@supabase/server/core' | ||
|
|
||
| // With a user's token (from verifyAuth) |
There was a problem hiding this comment.
This createContextClient snippet references auth.token / auth.keyName, but auth isn’t defined within the code block (it appears only in a separate snippet above). Consider making this snippet self-contained (include the verifyAuth(...) call in the same block) or replace auth.* with locally-defined placeholders like token/keyName.
| import { createContextClient } from '@supabase/server/core' | |
| // With a user's token (from verifyAuth) | |
| import { createContextClient, verifyAuth } from '@supabase/server/core' | |
| // With a user's token (from verifyAuth) | |
| const { auth } = await verifyAuth(request) |
| const { data: ctx, error } = await createSupabaseContext(req, { | ||
| allow: 'user', | ||
| }) | ||
|
|
||
| if (error) { | ||
| // Custom error format | ||
| return Response.json( | ||
| { | ||
| success: false, | ||
| error: { message: error.message, code: error.code }, | ||
| }, | ||
| { status: error.status }, | ||
| ) | ||
| } | ||
|
|
There was a problem hiding this comment.
In this snippet, destructuring { data: ctx, error } from the createSupabaseContext union result breaks TypeScript narrowing, so ctx remains typed as SupabaseContext | null even after if (error) return .... Prefer keeping the result object intact (e.g., const result = await createSupabaseContext(...)) or add an explicit non-null assertion/check before using ctx.supabase so the example type-checks under strictNullChecks.
| const { data: ctx, error } = await createSupabaseContext(req, { | |
| allow: 'user', | |
| }) | |
| if (error) { | |
| // Custom error format | |
| return Response.json( | |
| { | |
| success: false, | |
| error: { message: error.message, code: error.code }, | |
| }, | |
| { status: error.status }, | |
| ) | |
| } | |
| const result = await createSupabaseContext(req, { | |
| allow: 'user', | |
| }) | |
| if (result.error) { | |
| // Custom error format | |
| return Response.json( | |
| { | |
| success: false, | |
| error: { message: result.error.message, code: result.error.code }, | |
| }, | |
| { status: result.error.status }, | |
| ) | |
| } | |
| const ctx = result.data! |
| const { data: ctx, error } = await createSupabaseContext(req, { | ||
| allow: 'user', | ||
| }) | ||
|
|
||
| if (error) { | ||
| return Response.json( | ||
| { message: error.message, code: error.code }, | ||
| { status: error.status }, | ||
| ) | ||
| } | ||
|
|
There was a problem hiding this comment.
This createSupabaseContext example destructures { data: ctx, error } from a union return type; with strictNullChecks, ctx remains SupabaseContext | null even after the if (error) { return ... } guard. To keep the snippet copy/paste-able, either avoid destructuring (check result.error then use result.data) or assert ctx non-null before ctx.supabase....
| const { data: ctx, error } = await createSupabaseContext(req, { | |
| allow: 'user', | |
| }) | |
| if (error) { | |
| return Response.json( | |
| { message: error.message, code: error.code }, | |
| { status: error.status }, | |
| ) | |
| } | |
| const result = await createSupabaseContext(req, { | |
| allow: 'user', | |
| }) | |
| if (result.error) { | |
| return Response.json( | |
| { message: result.error.message, code: result.error.code }, | |
| { status: result.error.status }, | |
| ) | |
| } | |
| const ctx = result.data |
| } | ||
|
|
||
| // With partial overrides | ||
| const { data: env } = resolveEnv({ |
There was a problem hiding this comment.
In this code block, env is declared twice (const { data: env, error } = ... and later const { data: env } = ...) which will fail if someone copies the snippet verbatim. Consider renaming the second variable (e.g., envWithOverrides) or splitting into two separate code blocks.
| const { data: env } = resolveEnv({ | |
| const { data: envWithOverrides } = resolveEnv({ |
| const { data: auth, error } = await verifyCredentials(credentials, { | ||
| allow: 'user', | ||
| }) | ||
|
|
||
| if (error) { | ||
| return Response.json({ message: error.message }, { status: error.status }) | ||
| } | ||
|
|
There was a problem hiding this comment.
This example destructures { data: auth, error } from verifyCredentials(...); under strictNullChecks, auth stays typed as AuthResult | null even after if (error) return ..., so auth.authType / auth.userClaims won’t type-check. Using const result = await verifyCredentials(...); if (result.error) ...; const auth = result.data; (or asserting non-null) makes the snippet copy/paste-able.
| const { data: auth, error } = await verifyCredentials(credentials, { | |
| allow: 'user', | |
| }) | |
| if (error) { | |
| return Response.json({ message: error.message }, { status: error.status }) | |
| } | |
| const result = await verifyCredentials(credentials, { | |
| allow: 'user', | |
| }) | |
| if (result.error) { | |
| return Response.json( | |
| { message: result.error.message }, | |
| { status: result.error.status } | |
| ) | |
| } | |
| const auth = result.data |
| } | ||
|
|
||
| // With overrides | ||
| const { data: env } = resolveEnv({ |
There was a problem hiding this comment.
This snippet redeclares env in the same scope (const { data: env, error } = ... then later const { data: env } = ...), which will break if copy/pasted as-is. Rename the second variable or split into separate code blocks so it runs without edits.
| const { data: env } = resolveEnv({ | |
| const { data: envWithOverrides } = resolveEnv({ |
…ontext in examples
- Add non-null assertions (!) after error guards where TS can't narrow
destructured result tuples
- Split duplicate variable declarations into separate code blocks
- Add missing imports and show where variables like `auth` come from
- Keep { data, error } destructuring pattern consistent with SDK convention
- getting-started: replace Edge Function framing with runtime-neutral language, explain module worker pattern works across Deno/Bun/Workers, add Runtimes section covering all supported environments - webhooks: replace Deno.env with process.env for portable examples - environment-variables: add "Auto-injected in" column distinguishing Platform vs Local CLI, reframe section headers - auth-modes: clean up example key values - core-primitives: clarify "Integration with frameworks" wording - types: simplify TSDoc for publishable/secret key descriptions
Add docs/ssr-frameworks.md covering the pattern for using core primitives in Next.js, SvelteKit, Nuxt, and Remix — cookie extraction, env bridging, JWKS caching, and a complete Next.js adapter example. Replace the basic SSR example in core-primitives.md with a pointer to the new dedicated doc. Add SSR row to SKILL.md routing table.
Split the single generic example into per-platform sections (Edge Functions, Cloudflare Workers, Hono, SSR Frameworks) so AI agents pick the correct import specifier for each runtime. Adds npm: prefix to all Deno examples and a Deno column to the entry points table. Also adds createSupabaseContext examples.
Add secret key auth and webhook signature verification quick starts to SKILL.md. Add explicit decision tree for allow:'always' so AI agents confirm with the user before leaving endpoints unprotected.
- Add legacy keys warning to SKILL.md (avoid anon/service_role keys) - Add AI coding skills install section to README - Add server-to-server quick start with caller code to README - Add runtimes, documentation table, and named secret keys to README - Remove verifyWebhookSignature references from all docs - Delete docs/webhooks.md (code removal in separate PR)
Edge Functions require verify_jwt = false in config.toml when using allow: public, secret, or always — otherwise the platform rejects requests before the handler runs.
Add recipes for function-to-function calls, pg_net from database, Stripe webhooks, and generic webhook signature verification. Document the @supabase/server/wrappers entry point. Refactor environment-variables.md into Supabase vs non-Supabase sections.
This envs will be injected from cli too
| 1. `Deno.env.get(name)` — Deno (including Supabase Edge Functions) | ||
| 2. `process.env[name]` — Node.js, Bun, Cloudflare Workers (with node-compat) | ||
|
|
||
| ### Deno / Supabase Edge Functions |
There was a problem hiding this comment.
Not really sure if mentioning as "Deno / Supabase ...." will create confusion about "Vanilla Deno" exposing theses vars.
There was a problem hiding this comment.
Notice its mentioned at the end of "getting-started" docs
c433271 to
9972478
Compare
9972478 to
c1ba488
Compare
Summary
SKILL.mdas a lightweight entry point that routes agents to the right doc filedocs/folder with 11 focused, self-contained documentation files covering the full SDK surfacedocs/andSKILL.mdwith the npm package (filesin package.json) so agents can find them innode_modules/@supabase/server/Documentation files
getting-started.mdauth-modes.mdhono-adapter.mdcore-primitives.mdssr-frameworks.mdenvironment-variables.mderror-handling.mdsecurity.mdtypescript-generics.mdapi-reference.md