Files
supabase__server/docs/typescript-generics.md
Tomás Pozo 661329bb9e docs: add SDK documentation and SKILL.md (#20)
* docs: add initial documentation and skills.md

* docs: apply formatting

* docs: update SKILL.md to resolve docs from package location and ship 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.

* docs: add missing HTTPException import in error-handling example

* docs: fix strictNullChecks issues, duplicate variables, and missing context 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

* docs: reframe as runtime-agnostic and add env auto-injection details

- 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

* docs: add SSR frameworks guide and update references

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.

* docs: add disclaimer of new package

* docs: extend explanation on keys env vars

* docs: add platform-specific quick starts to SKILL.md

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.

* docs: add server-to-server quick starts and allow:always guardrails

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.

* docs: add legacy keys warning, skills install, remove webhook docs

- 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)

* docs: add verify_jwt = false note for non-user auth modes

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.

* docs: add edge function recipes and refactor env vars doc

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.

* docs: add security doc covering timing-safe comparison, auth model, CORS

* docs: link auth-modes timing-safe mentions to security.md

* docs: adding 'local cli' to secrets table

This envs will be injected from cli too

* docs: setting Deno as first installation choice

* docs: adding 'verify_jwt=false' disclaimer for non-user auth

* docs: split Deno/Supabase runtime section, merge Deno/Node/Bun

* docs(skills): adding legacy code migration example

* docs(skills): explaining why legacy code should be migrated

* docs: rewrite migration section, improve skill description triggers

---------

Co-authored-by: Kalleby Santos <kalleby_santos@hotmail.com>
2026-03-31 16:14:55 -05:00

3.9 KiB

TypeScript Generics

Overview

All client-creating functions accept a Database generic parameter. When you pass your generated database types, every .from('table').select() call is fully typed — column names, return types, insert shapes, and RPC signatures.

Generating types

Use the Supabase CLI to generate TypeScript types from your database schema:

npx supabase gen types typescript --project-id your-project-ref > src/database.types.ts

This produces a Database type that describes your schema.

Using with withSupabase

import { withSupabase } from '@supabase/server'
import type { Database } from './database.types.ts'

export default {
  fetch: withSupabase<Database>({ allow: 'user' }, async (_req, ctx) => {
    // ctx.supabase is SupabaseClient<Database>
    // Fully typed: column names, return type, etc.
    const { data } = await ctx.supabase
      .from('todos')
      .select('id, title, completed')
    // data is { id: number; title: string; completed: boolean }[] | null
    return Response.json(data)
  }),
}

Using with createSupabaseContext

import { createSupabaseContext } from '@supabase/server'
import type { Database } from './database.types.ts'

const { data: ctx, error } = await createSupabaseContext<Database>(request, {
  allow: 'user',
})

if (error) {
  throw error
}

// ctx.supabase and ctx.supabaseAdmin are both SupabaseClient<Database>
const { data } = await ctx!.supabase.from('profiles').select('id, email')

Using with core primitives

import {
  verifyAuth,
  createContextClient,
  createAdminClient,
} from '@supabase/server/core'
import type { Database } from './database.types.ts'

const { data: auth } = await verifyAuth(request, { allow: 'user' })

const supabase = createContextClient<Database>({
  auth: { token: auth!.token },
})

const supabaseAdmin = createAdminClient<Database>()

// Both clients are fully typed
const { data: todos } = await supabase.from('todos').select()
const { data: users } = await supabaseAdmin.from('profiles').select()

Using with the Hono adapter

The Hono context variable is typed as SupabaseContext (without the generic). To get typed clients, assert the type when destructuring:

import { Hono } from 'hono'
import { withSupabase } from '@supabase/server/adapters/hono'
import type { SupabaseContext } from '@supabase/server'
import type { Database } from './database.types.ts'

const app = new Hono()

app.use('*', withSupabase({ allow: 'user' }))

app.get('/todos', async (c) => {
  const { supabase } = c.var.supabaseContext as SupabaseContext<Database>
  const { data } = await supabase.from('todos').select('id, title')
  return c.json(data)
})

Custom schema

If your tables are in a schema other than public, pass it via supabaseOptions:

import { withSupabase } from '@supabase/server'
import type { Database } from './database.types.ts'

export default {
  fetch: withSupabase<Database>(
    {
      allow: 'user',
      supabaseOptions: { db: { schema: 'api' } },
    },
    async (_req, ctx) => {
      // Queries target the 'api' schema
      const { data } = await ctx.supabase.from('todos').select()
      return Response.json(data)
    },
  ),
}

Forwarding other Supabase client options

supabaseOptions accepts the same options as createClient() from @supabase/supabase-js, with two exceptions:

  • accessToken is stripped — token injection is managed by the SDK from verified credentials
  • Auth settings (persistSession, autoRefreshToken, detectSessionInUrl) are force-set to server-safe values
withSupabase<Database>(
  {
    allow: 'user',
    supabaseOptions: {
      db: { schema: 'api' },
      global: {
        headers: { 'x-custom-header': 'value' },
      },
    },
  },
  handler,
)

Note: Authorization and apikey headers in supabaseOptions.global.headers are sanitized (removed) to prevent overriding the verified credentials.