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
5 changes: 5 additions & 0 deletions apps/docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public/docs/
# Generated reference content (built by scripts/build-reference-content.ts)
/content/reference/

# Federated guide content, fetched from an external repo by
# scripts/federated-content/fetch-federated-content.ts
/content/guides/graphql/
/content/guides/graphql.mdx

# Downloaded TypeDoc dumps under spec/reference/<lib>/<ver>/. Regenerated by
# `cd apps/docs/spec && make download.tsdoc.v2`. Hand-authored files in the
# same folders (config.json, partials/) stay tracked.
Expand Down
202 changes: 15 additions & 187 deletions apps/docs/app/guides/graphql/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,198 +1,26 @@
import { isAbsolute, relative } from 'path'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
import {
genGuideMeta,
genGuidesStaticParams,
getGuidesMarkdown,
} from '~/features/docs/GuidesMdx.utils'
import { getEmptyArray } from '~/features/helpers.fn'
import { IS_DEV } from '~/lib/constants'
import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform'
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
import { getGitHubFileContents } from '~/lib/octokit'
import { SerializeOptions } from '~/types/next-mdx-remote-serialize'
import { notFound } from 'next/navigation'
import rehypeSlug from 'rehype-slug'

// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'pg_graphql'
const branch = 'master'
const docsDir = 'docs'
const externalSite = 'https://supabase.github.io/pg_graphql'

// Each external docs page is mapped to a local page
const pageMap = [
{
meta: {
id: 'graphql-overview',
title: 'GraphQL',
subtitle: 'Autogenerated GraphQL APIs with Postgres.',
},
remoteFile: 'supabase.md',
},
{
slug: 'api',
meta: {
id: 'graphql-api',
title: 'GraphQL API',
subtitle: 'Understanding the core concepts of the GraphQL API.',
},
remoteFile: 'api.md',
},
{
slug: 'views',
meta: {
id: 'graphql-views',
title: 'Views',
subtitle: 'Using Postgres Views with GraphQL.',
},
remoteFile: 'views.md',
},
{
slug: 'functions',
meta: {
id: 'graphql-functions',
title: 'Functions',
subtitle: 'Using Postgres Functions with GraphQL.',
},
remoteFile: 'functions.md',
},
{
slug: 'computed-fields',
meta: {
id: 'graphql-computed-fields',
title: 'Computed Fields',
subtitle: 'Using Postgres Computed Fields with GraphQL.',
},
remoteFile: 'computed_fields.md',
},
{
slug: 'configuration',
meta: {
id: 'graphql-configuration',
title: 'Configuration & Customization',
subtitle: 'Extra configuration options can be set on SQL entities using comment directives.',
},
remoteFile: 'configuration.md',
},
{
slug: 'security',
meta: {
id: 'graphql-security',
title: 'Security',
subtitle: 'Securing your GraphQL API.',
},
remoteFile: 'security.md',
},
{
slug: 'with-apollo',
meta: {
id: 'graphql-with-apollo',
title: 'With Apollo',
subtitle: 'Using pg_grapqhl with Apollo.',
},
remoteFile: 'usage_with_apollo.md',
},
{
slug: 'with-relay',
meta: {
id: 'graphql-with-relay',
title: 'With Relay',
subtitle: 'Using pg_grapqhl with Relay.',
},
remoteFile: 'usage_with_relay.md',
},
]

interface Params {
slug?: string[]
}
type Params = { slug?: string[] }

const PGGraphQLDocs = async (props: { params: Promise<Params> }) => {
const params = await props.params
const { meta, ...data } = await getContent(params)

const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions

return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}

/**
* Fetch markdown from external repo and transform links
*/
const getContent = async ({ slug }: Params) => {
const page = pageMap.find((page) => page.slug === slug?.at(0))

if (!page) {
notFound()
}

const { remoteFile, meta } = page

const editLink = newEditLink(`${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}`)

const content = await getGitHubFileContents({
org,
repo,
path: `${docsDir}/${remoteFile}`,
branch,
})

return {
pathname: `/guides/graphql${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`,
meta,
content,
editLink,
}
}

const urlTransform: UrlTransformFunction = (url) => {
try {
const externalSiteUrl = new URL(externalSite)

const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)

// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}

const getRelativePath = () => {
if (pathname.endsWith('.md')) {
return pathname.replace(/\.md$/, '')
}
if (isAbsolute(url)) {
return relative(externalSiteUrl.pathname, pathname)
}
return pathname
}

const relativePath = getRelativePath().replace(/^\//, '')

const page = pageMap.find(({ remoteFile }) => `${relativePath}.md` === remoteFile)

// If we have a mapping for this page, use the mapped path
if (page) {
return '/docs/guides/graphql/' + page.slug + hash
}
const slug = ['graphql', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)

// If we don't have this page in our docs, link to original docs
return `${externalSite}/${relativePath}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
return <GuideTemplate {...data!} />
}

const generateStaticParams = !IS_DEV
? async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
: getEmptyArray
const generateMetadata = genGuideMeta(getContent)
const generateStaticParams = !IS_DEV ? genGuidesStaticParams('graphql') : getEmptyArray
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['graphql', ...(params.slug ?? [])])
)

export default PGGraphQLDocs
export { generateMetadata, generateStaticParams }
export { generateStaticParams, generateMetadata }
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,10 @@ export const auth: NavMenuConstant = {
name: 'Flows (How-tos)',
enabled: authFlowsEnabled,
items: [
{
name: 'Which package to use',
url: '/guides/auth/choosing-a-server-package',
},
{
name: 'Server-Side Rendering',
url: '/guides/auth/server-side',
Expand Down
95 changes: 95 additions & 0 deletions apps/docs/content/guides/auth/choosing-a-server-package.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: 'Which package to use'
subtitle: 'When to use supabase-js, @supabase/ssr, or @supabase/server on the server.'
---

When you use Supabase from JavaScript on the server, there are three packages to choose from. They are not alternatives to each other — `@supabase/ssr` and `@supabase/server` both build on top of `supabase-js` and solve different problems. This guide helps you pick the right one.

<Admonition type="note">

These are JavaScript packages, not separate language SDKs. If you're looking for the client library for another language (Python, Swift, Kotlin, etc.), see the [client library references](/docs/reference).

</Admonition>

## Which package to use

The quickest way to decide is by **how the user's identity reaches your code**:

- The session lives in **cookies** (an SSR framework like Next.js or SvelteKit) → use **`@supabase/ssr`**.
- Auth arrives **per request in headers** (`Authorization: Bearer <jwt>`) → use **`@supabase/server`**.
- You want the base client, or you're handling auth yourself → use **`@supabase/supabase-js`** directly.

| Package | Use it when | Runs in | Auth model |
| ----------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------- |
| `@supabase/supabase-js` | You want the base client, or you manage auth yourself | Browser and server | You wire up auth |
| `@supabase/ssr` | User sessions are stored in **cookies** | SSR frameworks (Next.js, SvelteKit, TanStack Start) | Cookie-based sessions, with refresh-token rotation |
| `@supabase/server` | Auth arrives **per request in headers** | Edge Functions, Workers, Vercel, Bun, and framework APIs (Hono, H3, Elysia, NestJS) | Stateless Bearer JWT + `apikey` |

## `@supabase/supabase-js`

The isomorphic base client. `@supabase/ssr` and `@supabase/server` both wrap it — reach for `supabase-js` directly when you don't need either wrapper's auth handling.

```ts
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_PUBLISHABLE_KEY!)
```

## `@supabase/ssr`

For SSR frameworks that store the user's session in cookies. It reads and writes session cookies and handles refresh-token rotation, so the same user and session are available on both the client and the server.

```ts
import { createServerClient } from '@supabase/ssr'

const supabase = createServerClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_PUBLISHABLE_KEY!,
{
cookies: {
getAll() {
// return the request's cookies
},
setAll(cookiesToSet) {
// write cookies back on the response
},
},
}
)
```

See the [server-side rendering guide](/guides/auth/server-side) for framework-specific setup.

## `@supabase/server`

For stateless, header-based auth in backend runtimes — Edge Functions, Workers, and framework APIs. You declare who may call an endpoint and receive a ready-to-use context (a caller-scoped client that respects RLS, plus an admin client). It verifies JWTs and resolves the new API keys (`SUPABASE_PUBLISHABLE_KEYS` / `SUPABASE_SECRET_KEYS`) for you.

```ts
import { withSupabase } from '@supabase/server'

export default {
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
// ctx.supabase is scoped to the caller and respects RLS
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
}),
}
```

See the [`@supabase/server` reference](/docs/reference/server) for the full API.

<Admonition type="note">

`@supabase/ssr` and `@supabase/server` coexist and are not replacements for each other, and `@supabase/ssr` is not deprecated. Pick based on where your code runs and how auth reaches it, using the table above.

</Admonition>

## Advanced: Combining `@supabase/server` and `@supabase/ssr`

In a cookie-based framework you can compose the two — let `@supabase/ssr` own the cookie session lifecycle and hand the resolved token to `@supabase/server`'s primitives. This requires more setup, and deeper first-party integration is on the roadmap. See the [`@supabase/server` SSR frameworks guide](https://github.com/supabase/server/blob/main/docs/ssr-frameworks.md).

## Next steps

- [Server-side rendering](/guides/auth/server-side) — set up `@supabase/ssr` for your framework.
- [`@supabase/server` reference](/docs/reference/server) — API for header-based server auth.
- [`supabase-js` reference](/docs/reference/javascript) — the base JavaScript client.
4 changes: 2 additions & 2 deletions apps/docs/content/guides/auth/social-login/auth-apple.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ The platform-specific examples below demonstrate how to implement this pattern f
4. A **Services ID** which uniquely identifies the web services provided by the app you registered in the previous step. You can create a new Services ID from the [Identifiers](https://developer.apple.com/account/resources/identifiers/list/serviceId) section in the Apple Developer Console (use the filter menu in the upper right side to see all Services IDs). These usually are a reverse domain name string, for example `com.example.app.web`.
5. Configure Website URLs for the newly created **Services ID**. The web domain you should use is the domain your Supabase project is hosted on. This is usually `<project-id>.supabase.co` while the redirect URL is `https://<project-id>.supabase.co/auth/v1/callback`.
6. Create a signing **Key** in the [Keys](https://developer.apple.com/account/resources/authkeys/list) section of the Apple Developer Console. You can use this key to generate a secret key using the tool below, which is added to your Supabase project's Auth configuration. Make sure you safely store the `AuthKey_XXXXXXXXXX.p8` file. If you ever lose access to it, or make it public accidentally, revoke it from the Apple Developer Console and create a new one immediately.
7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](/dashboard/project/_/auth/providers).
7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](/dashboard/project/_/auth/providers). If your project also uses native Sign in with Apple (for example on iOS, Expo, or Flutter), list this Services ID as the **first** entry in the _Client IDs_ field. Supabase uses the first client ID in the list for the web `signInWithOAuth` flow, while the native `signInWithIdToken` flow accepts any client ID in the list as a valid token audience, regardless of order. If a native App ID comes before the Services ID, native sign-in keeps working but web sign-in is rejected by Apple.

You can also configure the Apple auth provider using the Management API:

Expand Down Expand Up @@ -508,7 +508,7 @@ curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
4. A **Services ID** which uniquely identifies the web services provided by the app you registered in the previous step. You can create a new Services ID from the [Identifiers](https://developer.apple.com/account/resources/identifiers/list/serviceId) section in the Apple Developer Console (use the filter menu in the upper right side to see all Services IDs). These usually are a reverse domain name string, for example `com.example.app.web`.
5. Configure Website URLs for the newly created **Services ID**. The web domain you should use is the domain your Supabase project is hosted on. This is usually `<project-id>.supabase.co` while the redirect URL is `https://<project-id>.supabase.co/auth/v1/callback`.
6. Create a signing **Key** in the [Keys](https://developer.apple.com/account/resources/authkeys/list) section of the Apple Developer Console. You can use this key to generate a secret key using the tool below, which is added to your Supabase project's Auth configuration. Make sure you safely store the `AuthKey_XXXXXXXXXX.p8` file. If you ever lose access to it, or make it public accidentally, revoke it from the Apple Developer Console and create a new one immediately. You will have to generate a new secret key using this file every 6 months, so make sure you schedule a recurring reminder in your calendar!
7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](/dashboard/project/_/auth/providers).
7. Finally, add the information you configured above to the [Apple provider configuration in the Supabase dashboard](/dashboard/project/_/auth/providers). If your app also uses native Sign in with Apple (on iOS or macOS), list this Services ID as the **first** entry in the _Client IDs_ field. Supabase uses the first client ID in the list for the web `signInWithOAuth` flow, while the native `signInWithIdToken` flow accepts any client ID in the list as a valid token audience, regardless of order. If a native App ID comes before the Services ID, native sign-in keeps working but web sign-in is rejected by Apple.

<Admonition type="tip">

Expand Down
5 changes: 5 additions & 0 deletions apps/docs/data/content-listings/auth.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const authGetStarted: ContentListingGroup = {
href: '/guides/auth/server-side',
description: 'Create a Supabase client for SSR frameworks like Next.js and SvelteKit.',
},
{
title: 'Which package to use',
href: '/guides/auth/choosing-a-server-package',
description: 'supabase-js vs @supabase/ssr vs @supabase/server — which to use on the server.',
},
{
title: 'Row Level Security',
href: '/guides/database/postgres/row-level-security',
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/docs/ref/javascript/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ hideTitle: true
This reference documents every object and method available in Supabase's isomorphic JavaScript library, `supabase-js`. You can use `supabase-js` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.

To convert SQL queries to `supabase-js` calls, use the [SQL to REST API translator](/docs/guides/api/sql-to-rest).

Using `supabase-js` on the server? See [which package to use](/docs/guides/auth/choosing-a-server-package) to decide between `supabase-js`, `@supabase/ssr`, and `@supabase/server`.
2 changes: 2 additions & 0 deletions apps/docs/docs/ref/server/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ title: Introduction
`@supabase/server` is a framework-agnostic library for authenticating requests in server-side JavaScript environments. It verifies JWTs, resolves Supabase API keys, and creates pre-configured Supabase clients — exposing everything through a single `SupabaseContext` that is identical regardless of which adapter or primitive produced it.

Adapters for Hono, H3, Elysia, and NestJS are included. You can also compose the lower-level primitives directly for custom frameworks or edge runtimes.

Not sure this is the right package? See [which package to use](/docs/guides/auth/choosing-a-server-package) — for cookie-based sessions in SSR frameworks, use [`@supabase/ssr`](/docs/guides/auth/server-side) instead.
Loading
Loading