mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Chore: update docs for new ssr package (#18455)
* update Next.js quickstart * update docs to use ssr package * update nav items to reflect new ssr pages * add migration guide from auth-helpers to ssr * add guides for astro and remix * add new pages to sitemap.xml * Update apps/docs/pages/guides/auth/server-side/overview.mdx Co-authored-by: Greg Richardson <greg.nmr@gmail.com> * Update apps/docs/pages/guides/auth/server-side/overview.mdx Co-authored-by: Greg Richardson <greg.nmr@gmail.com> * Update apps/docs/pages/guides/auth/server-side/creating-a-client.mdx Co-authored-by: Greg Richardson <greg.nmr@gmail.com> * Update apps/docs/pages/guides/auth/server-side/creating-a-client.mdx Co-authored-by: Greg Richardson <greg.nmr@gmail.com> * Add sveltekit example code * Add another admonition for sveltekit * Add express example * Fix spelling mistake * Run prettier to correct formatting * fix createClient examples for Next.js --------- Co-authored-by: Greg Richardson <greg.nmr@gmail.com> Co-authored-by: Andrew Smith <a.smith@silentworks.co.uk>
This commit is contained in:
15 files changed
+1332
-335
No files matched your search
@@ -548,22 +548,21 @@ export const auth = {
|
||||
name: 'Server-side Auth',
|
||||
url: undefined,
|
||||
items: [
|
||||
{ name: 'Overview', url: '/guides/auth/auth-helpers' },
|
||||
{ name: 'Overview', url: '/guides/auth/server-side/overview' },
|
||||
{ name: 'Creating a client', url: '/guides/auth/server-side/creating-a-client' },
|
||||
{
|
||||
name: 'Next.js',
|
||||
url: '/guides/auth/auth-helpers/nextjs',
|
||||
},
|
||||
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix' },
|
||||
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit' },
|
||||
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
|
||||
{
|
||||
name: 'Email Auth with PKCE flow for SSR',
|
||||
name: 'Email Auth with PKCE flow',
|
||||
url: '/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
{
|
||||
name: 'OAuth with PKCE flow for SSR',
|
||||
name: 'OAuth with PKCE flow',
|
||||
url: '/guides/auth/server-side/oauth-with-pkce-flow-for-ssr',
|
||||
},
|
||||
{ name: 'Server-side Rendering', url: '/guides/auth/server-side-rendering' },
|
||||
{
|
||||
name: 'Migrating from Auth Helpers',
|
||||
url: '/guides/auth/server-side/migrating-to-ssr-from-auth-helpers',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,11 +2,17 @@ import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'index',
|
||||
title: 'Server-Side Auth Overview',
|
||||
title: 'Auth Helpers',
|
||||
description: 'Server-Side Auth guides and utilities for working with Supabase.',
|
||||
sidebar_label: 'Overview',
|
||||
}
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
The `auth-helpers` packages have been deprecated in favor of the new `ssr` package. This takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
|
||||
|
||||
</Admonition>
|
||||
|
||||
Working with server-side frameworks is slightly different to client-side frameworks. In this section we cover the various ways of handling server-side authentication and demonstrate how to use the Supabase helper-libraries to make the process more seamless.
|
||||
|
||||
<div className="container" style={{ padding: 0 }}>
|
||||
|
||||
@@ -8,6 +8,12 @@ export const meta = {
|
||||
sidebar_label: 'Next.js (pages)',
|
||||
}
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
The `auth-helpers` packages have been deprecated in favor of the new `ssr` package. This takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
|
||||
|
||||
</Admonition>
|
||||
|
||||
This submodule provides convenience helpers for implementing user authentication in Next.js applications using the pages directory.
|
||||
|
||||
> Note: As of [Next.js 13.4](https://nextjs.org/blog/next-13-4), the App Router has reached stable status. This is now the recommended path for new Next.js app. Check out our guide on using [Auth Helpers with the Next.js App Directory](/docs/guides/auth/auth-helpers/nextjs).
|
||||
|
||||
@@ -8,6 +8,12 @@ export const meta = {
|
||||
sidebar_label: 'Next.js',
|
||||
}
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
The `auth-helpers` packages have been deprecated in favor of the new `ssr` package. This takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
|
||||
|
||||
</Admonition>
|
||||
|
||||
The [Next.js Auth Helpers package](https://github.com/supabase/auth-helpers) configures Supabase Auth to store the user's `session` in a `cookie`, rather than `localStorage`. This makes it available across the client and server of the App Router - [Client Components](/docs/guides/auth/auth-helpers/nextjs#client-components), [Server Components](/docs/guides/auth/auth-helpers/nextjs#server-components), [Server Actions](/docs/guides/auth/auth-helpers/nextjs#server-actions), [Route Handlers](/docs/guides/auth/auth-helpers/nextjs#route-handlers) and [Middleware](/docs/guides/auth/auth-helpers/nextjs#middleware). The `session` is automatically sent along with any requests to Supabase.
|
||||
|
||||
<div className="video-container">
|
||||
@@ -25,48 +31,13 @@ If you are using the `pages` directory, check out [Auth Helpers in Next.js Pages
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Automatic Configuration (recommended)
|
||||
|
||||
Use the `create-next-app` command and the `with-supabase` template to automate the configuration of cookie-based auth with the Next.js Auth Helpers.
|
||||
|
||||
```sh Terminal
|
||||
npx create-next-app -e with-supabase
|
||||
```
|
||||
|
||||
This creates a new Next.js app configured with:
|
||||
|
||||
- [Cookie-based Auth](/docs/guides/auth/auth-helpers/nextjs#authentication)
|
||||
- [Middleware to refresh user's session](/docs/guides/auth/auth-helpers/nextjs#managing-session-with-middleware)
|
||||
- [Code Exchange Route](/docs/guides/auth/auth-helpers/nextjs#Managing-sign-in-with-code-exchange)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
|
||||
### Declare Environment Variables
|
||||
|
||||
Retrieve your project's URL and anon key from your [API settings](https://supabase.com/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables:
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
```
|
||||
|
||||
### Run Next.js App
|
||||
|
||||
Run the development server and navigate to http://localhost:3000.
|
||||
|
||||
```sh Terminal
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Manual Configuration
|
||||
|
||||
### Install Next.js Auth Helpers library
|
||||
## Install Next.js Auth Helpers library
|
||||
|
||||
```sh Terminal
|
||||
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
||||
```
|
||||
|
||||
### Declare Environment Variables
|
||||
## Declare Environment Variables
|
||||
|
||||
Retrieve your project's URL and anon key from your [API settings](https://supabase.com/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables:
|
||||
|
||||
@@ -75,7 +46,7 @@ NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
|
||||
```
|
||||
|
||||
### Managing session with Middleware
|
||||
## Managing session with Middleware
|
||||
|
||||
When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
||||
|
||||
@@ -142,7 +113,7 @@ The `getSession` function must be called for any Server Component routes that us
|
||||
|
||||
</Admonition>
|
||||
|
||||
### Managing sign-in with Code Exchange
|
||||
## Managing sign-in with Code Exchange
|
||||
|
||||
The Next.js Auth Helpers are configured to use the [server-side auth flow](/docs/guides/auth/server-side-rendering) to sign users into your application. This requires you to setup a `Code Exchange` route, to exchange an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
|
||||
|
||||
|
||||
@@ -7,6 +7,12 @@ export const meta = {
|
||||
sidebar_label: 'Remix',
|
||||
}
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
The `auth-helpers` packages have been deprecated in favor of the new `ssr` package. This takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
|
||||
|
||||
</Admonition>
|
||||
|
||||
This submodule provides convenience helpers for implementing user authentication in Remix applications.
|
||||
|
||||
<div className="video-container">
|
||||
|
||||
@@ -7,6 +7,12 @@ export const meta = {
|
||||
sidebar_label: 'SvelteKit',
|
||||
}
|
||||
|
||||
<Admonition type="danger">
|
||||
|
||||
The `auth-helpers` packages have been deprecated in favor of the new `ssr` package. This takes the core concepts of the Auth Helpers package and makes them available to any server language or framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.
|
||||
|
||||
</Admonition>
|
||||
|
||||
This submodule provides convenience helpers for implementing user authentication in [SvelteKit](https://kit.svelte.dev/) applications.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -12,9 +12,9 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={1}>
|
||||
<StepHikeCompact.Details title="Create a new Supabase project">
|
||||
|
||||
[Launch a new project](https://supabase.com/dashboard) in the Supabase Dashboard.
|
||||
Head over to [database.new](https://database.new) and create a new Supabase project.
|
||||
|
||||
Your new database has a table for storing your users. You can see that this table is currently empty by running some SQL in the [SQL Editor](https://supabase.com/dashboard/project/_/sql).
|
||||
Your new database has a table for storing your users. You can see that this table is currently empty by running some SQL in the [SQL Editor](https://supabase.com/dashboard/project/_/sql/new).
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
@@ -33,7 +33,7 @@ export const meta = {
|
||||
<StepHikeCompact.Details title="Create a Next.js app">
|
||||
|
||||
Use the `create-next-app` command and the `with-supabase` template, to create a Next.js app pre-configured with:
|
||||
- [Supabase Auth](https://supabase.com/docs/guides/auth/auth-helpers/nextjs)
|
||||
- [Cookie-based Auth](https://supabase.com/docs/guides/auth/auth-helpers/nextjs)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
|
||||
@@ -42,7 +42,7 @@ export const meta = {
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npx create-next-app -e with-supabase my-app && cd my-app
|
||||
npx create-next-app -e with-supabase
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
|
||||
@@ -0,0 +1,817 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'Creating a Supabase client for SSR',
|
||||
description: 'Configure Supabase client to use Cookies',
|
||||
sidebar_label: 'Creating a client',
|
||||
}
|
||||
|
||||
### Install Supabase packages
|
||||
|
||||
The `ssr` package configures Supabase to use Cookies, which is required for server-side languages and frameworks.
|
||||
|
||||
```bash
|
||||
npm install @supabase/ssr @supabase/supabase-js
|
||||
```
|
||||
|
||||
### Set environment variables
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework">
|
||||
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
Create a `.env.local` file in your project root directory. You can get your `SUPABASE_URL` and `SUPABASE_ANON_KEY` from inside [your Supabase project's dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Create a `.env.local` file in your project root directory. You can get your `SUPABASE_URL` and `SUPABASE_ANON_KEY` from inside [your Supabase project's dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
```bash .env.local
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
Create a `.env` file in your project root directory. You can get your `SUPABASE_URL` and `SUPABASE_ANON_KEY` from inside [your Supabase project's dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
```bash .env
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
Create a `.env` file in your project root directory. You can get your `SUPABASE_URL` and `SUPABASE_ANON_KEY` from inside [your Supabase project's dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
```bash .env
|
||||
SUPABASE_URL=your_supabase_project_url
|
||||
SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
Create a `.env` file in your project root directory. You can get your `SUPABASE_URL` and `SUPABASE_ANON_KEY` from inside [your Supabase project's dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
```bash .env
|
||||
SUPABASE_URL=your_supabase_project_url
|
||||
SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
Install [dotenv](https://www.npmjs.com/package/dotenv)
|
||||
|
||||
```bash
|
||||
npm i dotenv
|
||||
```
|
||||
|
||||
and initialize it
|
||||
|
||||
```js app.js
|
||||
const express = require("express")
|
||||
const dotenv = require("dotenv")
|
||||
dotenv.config()
|
||||
...
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Creating a client
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
Creating a Supabase client with the `ssr` package automatically configures it to use cookies. This means your user's session is available throughout the entire Next.js stack - client, server, App Router, Pages Router. It just works!
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="client-component"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="client-component" label="Client Component">
|
||||
|
||||
```ts page.tsx
|
||||
"use client";
|
||||
|
||||
import { createBrowserClient } from '@supabase/ssr'
|
||||
|
||||
export default function Page () {
|
||||
const supabase = createBrowserClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||
)
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="server-component" label="Server Component">
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Server Components only have read access to cookies. Check the `Middleware` tab for an example of refreshing expired sessions before loading a Server Component route.
|
||||
|
||||
</Admonition>
|
||||
|
||||
```ts page.tsx
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
export default async function Page () {
|
||||
const cookieStore = cookies()
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="middleware" label="Middleware">
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Server Components only have read access to cookies. This `Middleware` example can be used to refresh expired sessions before loading Server Component routes.
|
||||
|
||||
</Admonition>
|
||||
|
||||
```ts middleware.ts
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
let response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
})
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return request.cookies.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
request.cookies.set({
|
||||
name,
|
||||
value,
|
||||
...options,
|
||||
})
|
||||
response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
})
|
||||
response.cookies.set({
|
||||
name,
|
||||
value,
|
||||
...options,
|
||||
})
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
request.cookies.set({
|
||||
name,
|
||||
value: '',
|
||||
...options,
|
||||
})
|
||||
response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
})
|
||||
response.cookies.set({
|
||||
name,
|
||||
value: '',
|
||||
...options,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
await supabase.auth.getSession()
|
||||
|
||||
return response
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="route-handler" label="Route Handler">
|
||||
|
||||
```ts route.js
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const cookieStore = cookies()
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value: '', ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="server-action" label="Server Action">
|
||||
|
||||
```ts page.tsx
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
|
||||
export default async function Page () {
|
||||
const serverAction = async () => {
|
||||
'use server'
|
||||
|
||||
const cookieStore = cookies()
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value: '', ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="get-server-side-props" label="getServerSideProps">
|
||||
|
||||
Cookies have many edge cases when not using a helper function, therefore we recommend using the `cookies` package in `getServerSideProps` to provide a similar API to the Next.js `cookies` function in the App Router.
|
||||
|
||||
```bash
|
||||
npm install cookies
|
||||
```
|
||||
|
||||
Types can optionally be installed as well.
|
||||
|
||||
```bash
|
||||
npm install --save-dev @types/cookies
|
||||
```
|
||||
|
||||
```ts index.tsx
|
||||
import { type GetServerSidePropsContext } from 'next'
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import Cookies from 'cookies'
|
||||
|
||||
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
|
||||
const cookies = new Cookies(context.req, context.res)
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookies.get(name)
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookies.set(name, value, options)
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookies.set(name, '', options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
return {...}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="get-static-props" label="getStaticProps">
|
||||
|
||||
The `getStaticProps` function gets executed at build time, rather than per request. At build time,
|
||||
there is no user, session or cookies. Therefore, we can use the standard `createClient` function
|
||||
from `supabase-js`.
|
||||
|
||||
```ts page.tsx
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const cookies = new Cookies(context.req, context.res)
|
||||
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
)
|
||||
|
||||
return {...}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="api-route" label="API Route">
|
||||
|
||||
Cookies have many edge cases when not using a helper function, therefore, we recommend using the `cookies` package in API Routes to provide a similar API to the Next.js `cookies` function in the App Router.
|
||||
|
||||
```bash
|
||||
npm install cookies
|
||||
```
|
||||
|
||||
Types can optionally be installed as well.
|
||||
|
||||
```bash
|
||||
npm install --save-dev @types/cookies
|
||||
```
|
||||
|
||||
```ts index.tsx
|
||||
import { createServerClient, type CookieOptions } from "@supabase/ssr"
|
||||
import { type NextApiRequest, type NextApiResponse } from "next"
|
||||
import Cookies from "cookies"
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const cookies = new Cookies(req, res)
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookies.get(name)
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookies.set(name, value, options)
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookies.set(name, "", options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
res.send(...)
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="pages-component" label="Pages Component">
|
||||
|
||||
```ts index.tsx
|
||||
import { createBrowserClient } from "@supabase/ssr"
|
||||
|
||||
export default function Index () {
|
||||
const supabase = createBrowserClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||
)
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Creating a Supabase client with the `ssr` package automatically configures it to use Cookies. This means your user's session is available throughout the entire SvelteKit stack - page, layout, server, hooks. It just works!
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="hooks"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="hooks" label="Hooks">
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
API routes, server layout load and form actions can now access the supabase client from the `event` object due to this hook.
|
||||
|
||||
</Admonition>
|
||||
|
||||
```ts hooks.server.ts
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||
cookies: {
|
||||
get: (key) => event.cookies.get(key),
|
||||
set: (key, value, options) => {
|
||||
event.cookies.set(key, value, options)
|
||||
},
|
||||
remove: (key, options) => {
|
||||
event.cookies.delete(key, options)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* a little helper that is written for convenience so that instead
|
||||
* of calling `const { data: { session } } = await supabase.auth.getSession()`
|
||||
* you just call this `await getSession()`
|
||||
*/
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="layout" label="Layout Load">
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
Page components can get access to the supabase client from the `data` object due to this load function.
|
||||
|
||||
</Admonition>
|
||||
|
||||
```ts +layout.ts
|
||||
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
|
||||
import type { LayoutLoad } from './$types'
|
||||
import { createBrowserClient, isBrowser, parse } from '@supabase/ssr'
|
||||
|
||||
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
|
||||
depends('supabase:auth')
|
||||
|
||||
const supabase = createBrowserClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
|
||||
global: {
|
||||
fetch,
|
||||
},
|
||||
cookies: {
|
||||
get(key) {
|
||||
if (!isBrowser()) {
|
||||
return JSON.stringify(data.session)
|
||||
}
|
||||
|
||||
const cookie = parse(document.cookie)
|
||||
return cookie[key]
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession()
|
||||
|
||||
return { supabase, session }
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="page-component" label="Page Component">
|
||||
|
||||
```svelte +page.svelte
|
||||
<script>
|
||||
export let data
|
||||
let { supabase } = data
|
||||
$: ({ supabase } = data)
|
||||
|
||||
...
|
||||
</script>
|
||||
|
||||
....
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="api-route" label="API route">
|
||||
|
||||
```ts +server.ts
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export const GET = async (event) => {
|
||||
const { url, locals: { supabase } } = event
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="form-actions" label="Form actions">
|
||||
|
||||
```ts +page.server.ts
|
||||
import type { Actions } from './$types'
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
const { request, url, locals: { supabase } } = event
|
||||
const formData = await request.formData()
|
||||
const email = formData.get('email') as string
|
||||
const password = formData.get('password') as string
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="astro-server"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="astro-server" label="Server">
|
||||
|
||||
```ts index.astro
|
||||
---
|
||||
import { createServerClient, type CookieOptions } from "@suapbase/ssr";
|
||||
|
||||
const supabase = createServerClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
cookies: {
|
||||
get(key: string) {
|
||||
return Astro.cookies.get(key)?.value;
|
||||
},
|
||||
set(key: string, value: string, options: CookieOptions) {
|
||||
Astro.cookies.set(key, value, options);
|
||||
},
|
||||
remove(key: string, options) {
|
||||
Astro.cookies.delete(key, options);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
---
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="astro-browser" label="Browser">
|
||||
|
||||
```html index.astro
|
||||
<script>
|
||||
const supabase = createBrowserClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY
|
||||
);
|
||||
</script>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="remix-loader"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="remix-loader" label="Loader">
|
||||
|
||||
```ts _index.tsx
|
||||
import { type LoaderFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
const headers = new Headers()
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return new Response('...', {
|
||||
headers,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix-action" label="Action">
|
||||
|
||||
```ts _index.tsx
|
||||
import { type ActionFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
|
||||
export async function action({ request }: ActionFunctionArgs) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
const headers = new Headers()
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return new Response('...', {
|
||||
headers,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix-component" label="Component">
|
||||
|
||||
```ts _index.tsx
|
||||
import { type LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { useLoaderData } from "@remix-run/react";
|
||||
import { createBrowserClient } from "@supabase/ssr";
|
||||
|
||||
export async function loader({}: LoaderFunctionArgs) {
|
||||
return {
|
||||
env: {
|
||||
SUPABASE_URL: process.env.SUPABASE_URL!,
|
||||
SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY!,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function Index() {
|
||||
const { env } = useLoaderData<typeof loader>();
|
||||
|
||||
const supabase = createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY);
|
||||
|
||||
return ...
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="server-client"
|
||||
queryGroup="environment"
|
||||
>
|
||||
<TabPanel id="server-client" label="Server Client">
|
||||
|
||||
```ts lib/supabase.js
|
||||
const { createServerClient } = require('@supabase/ssr')
|
||||
|
||||
exports.createClient = (context) => {
|
||||
return createServerClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, {
|
||||
cookies: {
|
||||
get: (key) => {
|
||||
const cookies = context.req.cookies
|
||||
const cookie = cookies[key] ?? ''
|
||||
return decodeURIComponent(cookie)
|
||||
},
|
||||
set: (key, value, options) => {
|
||||
if (!context.res) return
|
||||
context.res.cookie(key, encodeURIComponent(value), {
|
||||
...options,
|
||||
sameSite: 'Lax',
|
||||
httpOnly: true,
|
||||
})
|
||||
},
|
||||
remove: (key, options) => {
|
||||
if (!context.res) return
|
||||
context.res.cookie(key, '', { ...options, httpOnly: true })
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express-route" label="Route">
|
||||
|
||||
```ts app.js
|
||||
const express = require("express")
|
||||
const dotenv = require("dotenv")
|
||||
|
||||
const { createClient } = require("./lib/supabase")
|
||||
|
||||
const app = express()
|
||||
|
||||
app.post("/hello-world", async function (req, res, next) {
|
||||
const { email, emailConfirm } = req.body
|
||||
...
|
||||
|
||||
const supabase = createClient({ req, res })
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
## Next steps
|
||||
|
||||
- Implement [Authentication using Email and Password](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
|
||||
- Implement [Authentication using OAuth](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
|
||||
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
+165
-120
@@ -8,116 +8,9 @@ export const meta = {
|
||||
'Learn how to configure email authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
}
|
||||
|
||||
### Install Supabase Auth Helpers
|
||||
### Setting up SSR client
|
||||
|
||||
The Auth Helpers will assist you in implementing user authentication within your server-side rendering (SSR) framework.
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework">
|
||||
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Set environment variables
|
||||
|
||||
Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework">
|
||||
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash .env.local
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Setting up the Auth Helpers
|
||||
|
||||
When using the Supabase client on the server, you must perform extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.
|
||||
|
||||
Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.
|
||||
|
||||
Create a new `middleware.js` file in the root of your project and populate with the following:
|
||||
|
||||
```js middleware.js
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function middleware(req) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new `hooks.server.js` file in the root of your project and populate with the following:
|
||||
|
||||
```ts src/hooks.server.js
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
|
||||
export const handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event,
|
||||
})
|
||||
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
Check out our [guide for creating a client](/docs/guides/auth/server-side/creating-a-client) to learn how to install the necessary packages, declare environment variables, and create a Supabase client configured for SSR in your framework.
|
||||
|
||||
### Create API endpoint for handling `token_hash`
|
||||
|
||||
@@ -130,36 +23,59 @@ In order to use the updated email links we will need to setup a endpoint for ver
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Create a new file at `app/auth/confirm/route.js` and populate with the following:
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
```js app/auth/confirm/route.js
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
Create a new file at `app/auth/confirm/route.ts` and populate with the following:
|
||||
|
||||
```ts app/auth/confirm/route.ts
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(req) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type')
|
||||
const type = searchParams.get('type') as EmailOtpType | null
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookieStore = cookies()
|
||||
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
||||
const { error } = await supabase.auth.verifyOtp({ type, token_hash })
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.delete({ name, ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, request.url))
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', request.url))
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Create a new file at `src/routes/auth/confirm/+server.js` and populate with the following:
|
||||
|
||||
```js src/routes/auth/confirm/+server.js
|
||||
@@ -186,6 +102,135 @@ export const GET = async (event) => {
|
||||
};
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
Create a new file at `src/pages/auth/confirm.ts` and populate with the following:
|
||||
|
||||
```ts src/pages/auth/confirm.ts
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { type APIRoute } from 'astro'
|
||||
|
||||
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createServerClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies.get(key)?.value
|
||||
},
|
||||
set(key, value, options) {
|
||||
cookies.set(key, value, options)
|
||||
},
|
||||
remove(key, options) {
|
||||
cookies.delete(key, options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(`/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
Create a new file at `app/routes/auth.confirm.tsx` and populate with the following:
|
||||
|
||||
```ts app/routes/auth.confirm.tsx
|
||||
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const token_hash = requestUrl.searchParams.get('token_hash')
|
||||
const type = requestUrl.searchParams.get('type') as EmailOtpType | null
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (token_hash && type) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
const headers = new Headers()
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
|
||||
if (!error) {
|
||||
return redirect(`/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
Create a new route in your express app and populate with the following:
|
||||
|
||||
```js app.js
|
||||
...
|
||||
app.get("/auth/confirm", async function (req, res) {
|
||||
const token_hash = req.query.token_hash
|
||||
const type = req.query.type
|
||||
const next = req.query.next ?? "/"
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = createClient({ req, res })
|
||||
const { error } = await supabase.auth.verifyOtp({
|
||||
type,
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
res.redirect(303, `/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
res.redirect(303, '/auth/auth-code-error')
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
title: 'Migrating to the SSR package from Auth Helpers',
|
||||
description: 'Step-by-step guide to migrating your app to the new SSR package',
|
||||
sidebar_label: 'Migrating to SSR from Auth Helpers',
|
||||
}
|
||||
|
||||
The new `ssr` package takes the core concepts of the Auth Helpers and makes them available to any server language or framework. This page will guide you through migrating from the Auth Helpers package to `ssr`.
|
||||
|
||||
### Replacing Supabase packages
|
||||
|
||||
<Tabs scrollable size="small" type="underlined" defaultActiveId="nextjs" queryGroup="framework">
|
||||
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
```bash
|
||||
npm uninstall @supabase/auth-helpers-nextjs
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash
|
||||
npm uninstall @supabase/auth-helpers-sveltekit
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
```bash
|
||||
npm uninstall @supabase/auth-helpers-remix
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
```bash
|
||||
npm install @supabase/ssr
|
||||
```
|
||||
|
||||
### Creating a client
|
||||
|
||||
The new `ssr` package exports two functions for creating a Supabase client. The `createBrowserClient` function is used in the client, and the `createServerClient` function is used in the server.
|
||||
|
||||
Check out the [Creating a client](/docs/guides/auth/server-side/creating-a-client) page for examples of creating a client in your framework.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Implement [Authentication using Email and Password](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
|
||||
- Implement [Authentication using OAuth](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
|
||||
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -8,127 +8,9 @@ export const meta = {
|
||||
'Learn how to configure OAuth authentication in your server-side rendering (SSR) application to work with the PKCE flow.',
|
||||
}
|
||||
|
||||
### Install Supabase Auth Helpers
|
||||
### Setting up SSR client
|
||||
|
||||
The Auth Helpers assist with user authentication within server-side rendering (SSR) frameworks.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash
|
||||
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Set environment variables
|
||||
|
||||
Create an `.env.local` file in your project root directory. You can get your `SITE_URL` and `ANON_KEY` from inside of the [dashboard](https://supabase.com/dashboard/project/_/settings/api).
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
|
||||
```bash .env.local
|
||||
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
```bash .env.local
|
||||
PUBLIC_SUPABASE_URL=your_supabase_project_url
|
||||
PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
### Setting up the Auth Helpers
|
||||
|
||||
For SSR, the Supabase client requires extra steps to ensure the user's auth session remains active. Since the user's session is tracked in a cookie, we need to read this cookie and update it if necessary.
|
||||
|
||||
<Tabs
|
||||
scrollable
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Next.js Server Components allow you to read a cookie but not write back to it. Middleware on the other hand allow you to both read and write to cookies.
|
||||
|
||||
Next.js [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route is rendered. We'll use Middleware to refresh the user's session before loading Server Component routes.
|
||||
|
||||
Create a new `middleware.js` file in the root of your project and populate with the following:
|
||||
|
||||
```js middleware.js
|
||||
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function middleware(req) {
|
||||
const res = NextResponse.next()
|
||||
const supabase = createMiddlewareClient({ req, res })
|
||||
await supabase.auth.getSession()
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
Create a new `hooks.server.js` file in the root of your project and populate with the following:
|
||||
|
||||
```ts src/hooks.server.js
|
||||
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
|
||||
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
|
||||
import type { Handle } from '@sveltejs/kit'
|
||||
import { dev } from '$app/environment'
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.supabase = createSupabaseServerClient({
|
||||
supabaseUrl: PUBLIC_SUPABASE_URL,
|
||||
supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
|
||||
event,
|
||||
cookieOptions: { secure: !dev },
|
||||
})
|
||||
|
||||
event.locals.getSession = async () => {
|
||||
const {
|
||||
data: { session },
|
||||
} = await event.locals.supabase.auth.getSession()
|
||||
return session
|
||||
}
|
||||
|
||||
return resolve(event, {
|
||||
filterSerializedResponseHeaders(name) {
|
||||
return name === 'content-range'
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
Check out our [guide for creating a client](/docs/guides/auth/server-side/creating-a-client) to learn how to install the necessary packages, declare environment variables, and create a Supabase client configured for SSR in your framework.
|
||||
|
||||
### Create API endpoint for handling the `code` exchange
|
||||
|
||||
@@ -141,35 +23,55 @@ In order to use OAuth we will need to setup a endpoint for the `code` exchange,
|
||||
defaultActiveId="nextjs"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="nextjs" label="NextJS">
|
||||
Create a new file at `app/auth/callback/route.js` and populate with the following:
|
||||
<TabPanel id="nextjs" label="Next.js">
|
||||
|
||||
```js app/auth/callback/route.js
|
||||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
|
||||
Create a new file at `app/auth/callback/route.ts` and populate with the following:
|
||||
|
||||
```ts app/auth/callback/route.ts
|
||||
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function GET(req) {
|
||||
const { searchParams } = new URL(req.url)
|
||||
export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const code = searchParams.get('code')
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
|
||||
if (code) {
|
||||
const cookieStore = cookies()
|
||||
const supabase = createRouteHandlerClient({ cookies: () => cookieStore })
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
get(name: string) {
|
||||
return cookieStore.get(name)?.value
|
||||
},
|
||||
set(name: string, value: string, options: CookieOptions) {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
cookieStore.delete({ name, ...options })
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
if (!error) {
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, req.url))
|
||||
return NextResponse.redirect(new URL(`/${next.slice(1)}`, request.url))
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', req.url))
|
||||
return NextResponse.redirect(new URL('/auth/auth-code-error', request.url))
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="sveltekit" label="SvelteKit">
|
||||
|
||||
Create a new file at `src/routes/auth/callback/+server.js` and populate with the following:
|
||||
|
||||
```js src/routes/auth/callback/+server.js
|
||||
@@ -195,6 +97,117 @@ export const GET = async (event) => {
|
||||
};
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="astro" label="Astro">
|
||||
|
||||
Create a new file at `src/pages/auth/callback.ts` and populate with the following:
|
||||
|
||||
```ts src/pages/auth/callback.ts
|
||||
import { createServerClient } from '@supabase/ssr'
|
||||
import { type APIRoute } from 'astro'
|
||||
|
||||
export const GET: APIRoute = async ({ request, cookies, redirect }) => {
|
||||
const requestUrl = new URL(request.url)
|
||||
const code = requestUrl.searchParams.get('code')
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (code) {
|
||||
const supabase = createServerClient(
|
||||
import.meta.env.PUBLIC_SUPABASE_URL,
|
||||
import.meta.env.PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies.get(key)?.value
|
||||
},
|
||||
set(key, value, options) {
|
||||
cookies.set(key, value, options)
|
||||
},
|
||||
remove(key, options) {
|
||||
cookies.delete(key, options)
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
|
||||
if (!error) {
|
||||
return redirect(`/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel id="remix" label="Remix">
|
||||
|
||||
Create a new file at `app/routes/auth.callback.tsx` and populate with the following:
|
||||
|
||||
```ts app/routes/auth.callback.tsx
|
||||
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
|
||||
import { createServerClient, parse, serialize } from '@supabase/ssr'
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
const requestUrl = new URL(request.url)
|
||||
const code = requestUrl.searchParams.get('code')
|
||||
const next = requestUrl.searchParams.get('next') || '/'
|
||||
|
||||
if (code) {
|
||||
const cookies = parse(request.headers.get('Cookie') ?? '')
|
||||
const headers = new Headers()
|
||||
|
||||
const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, {
|
||||
cookies: {
|
||||
get(key) {
|
||||
return cookies[key]
|
||||
},
|
||||
set(key, value, options) {
|
||||
headers.append('Set-Cookie', serialize(key, value, options))
|
||||
},
|
||||
remove(key, options) {
|
||||
headers.append('Set-Cookie', serialize(key, '', options))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { error } = await supabase.auth.exchangeCodeForSession(code)
|
||||
|
||||
if (!error) {
|
||||
return redirect(`/${next.slice(1)}`)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with instructions
|
||||
return redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="express" label="Express">
|
||||
|
||||
Create a new route in your express app and populate with the following:
|
||||
|
||||
```js app.js
|
||||
...
|
||||
app.get("/auth/callback", async function (req, res) {
|
||||
const code = req.query.code
|
||||
const next = req.query.next ?? "/"
|
||||
|
||||
if (code) {
|
||||
const supabase = createClient({ req, res })
|
||||
await supabase.auth.exchangeCodeForSession(code)
|
||||
}
|
||||
|
||||
res.redirect(303, `/${next.slice(1)}`)
|
||||
})
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import Layout from '~/layouts/DefaultGuideLayout'
|
||||
|
||||
export const meta = {
|
||||
id: 'index',
|
||||
title: 'Server-Side Auth Overview',
|
||||
description: 'Configure Supabase Auth to use Cookies',
|
||||
sidebar_label: 'Overview',
|
||||
}
|
||||
|
||||
When using Supabase with server-side languages and frameworks - such as Next.js, SvelteKit and Remix - it is important to configure your Supabase client to use cookies for storing user sessions. We have developed an `@supabase/ssr` package to make this process as simple as possible. This package is currently in `beta`, and is likely to have breaking changes in the future.
|
||||
|
||||
## Framework Quickstarts
|
||||
|
||||
<div className="grid lg:grid-cols-12 gap-6 not-prose">
|
||||
{quickstarts.map((item) => {
|
||||
return (
|
||||
<Link href={`${item.href}`} key={item.title} passHref>
|
||||
<a className={'col-span-4'}>
|
||||
<GlassPanel title={item.title} span="col-span-6" background={false} icon={item.icon}>
|
||||
{item.description}
|
||||
</GlassPanel>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
If you're currently using the [Auth Helpers package](https://github.com/supabase/auth-helpers), the [docs are still available](/docs/guides/auth/auth-helpers), however we recommend migrating to the new `@supabase/ssr` package as this will be the recommended path moving forward.
|
||||
|
||||
</Admonition>
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Create a Supabase client for SSR](/docs/guides/auth/server-side/creating-a-client)
|
||||
- [Email Auth with PKCE flow for SSR](/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr)
|
||||
- [OAuth with PKCE flow for SSR](/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr)
|
||||
- [Learn more about SSR](/docs/guides/auth/server-side-rendering)
|
||||
|
||||
export const quickstarts = [
|
||||
{
|
||||
title: 'Next.js',
|
||||
href: '/guides/auth/quickstarts/nextjs',
|
||||
description:
|
||||
'Automatically configure Supabase to use cookies, making your user and their session available on the client and server.',
|
||||
icon: '/docs/img/icons/nextjs-icon',
|
||||
},
|
||||
]
|
||||
|
||||
export const Page = ({ children }) => <Layout meta={meta} children={children} />
|
||||
|
||||
export default Page
|
||||
@@ -137,10 +137,10 @@ export const quickstarts = [
|
||||
icon: '/docs/img/icons/react-icon',
|
||||
},
|
||||
{
|
||||
title: 'NextJS',
|
||||
title: 'Next.js',
|
||||
href: '/guides/getting-started/quickstarts/nextjs',
|
||||
description:
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a NextJS app.',
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Next.js app.',
|
||||
icon: '/docs/img/icons/nextjs-icon',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,20 +4,20 @@ import StepHikeCompact from '~/components/StepHikeCompact'
|
||||
export const meta = {
|
||||
title: 'Use Supabase with Next.js',
|
||||
subtitle:
|
||||
'Learn how to create a Supabase project, add some sample data to your database, and query the data from a Next.js app.',
|
||||
'Learn how to create a Supabase project, add some sample data, and query from a Next.js app.',
|
||||
breadcrumb: 'Framework Quickstarts',
|
||||
}
|
||||
|
||||
<StepHikeCompact>
|
||||
|
||||
<StepHikeCompact.Step step={1}>
|
||||
<StepHikeCompact.Details title="Set up a Supabase project with sample data">
|
||||
<StepHikeCompact.Details title="Set up a Supabase project">
|
||||
|
||||
[Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard.
|
||||
Head over to [database.new](https://database.new) and create a new Supabase project.
|
||||
|
||||
After your project is ready, create a table in your Supabase database using the [SQL Editor](https://supabase.com/dashboard/project/_/sql) in the Dashboard.
|
||||
When your project is up and running, navigate to the [Table Editor](https://supabase.com/dashboard/project/_/editor), create a new table and insert some data.
|
||||
|
||||
Use the following SQL statement to create a `countries` table with some sample data.
|
||||
Alternatively, you can run the following snippet in your project's [SQL Editor](https://supabase.com/dashboard/project/_/sql/new). This will create a `notes` table with some sample data.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
@@ -25,15 +25,17 @@ export const meta = {
|
||||
|
||||
```sql SQL_EDITOR
|
||||
-- Create the table
|
||||
create table countries (
|
||||
create table notes (
|
||||
id serial primary key,
|
||||
name text not null
|
||||
title text
|
||||
);
|
||||
|
||||
-- Insert some sample data into the table
|
||||
insert into countries (name) values ('United States');
|
||||
insert into countries (name) values ('Canada');
|
||||
insert into countries (name) values ('Mexico');
|
||||
-- Insert some sample data
|
||||
insert into notes (title)
|
||||
values
|
||||
('Today I created a Supabase project.'),
|
||||
('I added some data and queried it from Next.js.'),
|
||||
('It was awesome!');
|
||||
````
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
@@ -45,7 +47,7 @@ export const meta = {
|
||||
<StepHikeCompact.Details title="Create a Next.js app">
|
||||
|
||||
Use the `create-next-app` command and the `with-supabase` template, to create a Next.js app pre-configured with:
|
||||
- [Supabase Auth](https://supabase.com/docs/guides/auth/auth-helpers/nextjs)
|
||||
- [Cookie-based Auth](https://supabase.com/docs/guides/auth/auth-helpers/nextjs)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
|
||||
@@ -54,7 +56,7 @@ export const meta = {
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```bash Terminal
|
||||
npx create-next-app -e with-supabase my-app && cd my-app
|
||||
npx create-next-app -e with-supabase
|
||||
```
|
||||
|
||||
</StepHikeCompact.Code>
|
||||
@@ -82,31 +84,24 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={4}>
|
||||
<StepHikeCompact.Details title="Query Supabase data from Next.js">
|
||||
|
||||
Create a new file at `app/countries/page.tsx` and populate with the following.
|
||||
Create a new file at `app/notes/page.tsx` and populate with the following.
|
||||
|
||||
This will select all the rows from the `countries` table in Supabase and render them on the page.
|
||||
This will select all the rows from the `notes` table in Supabase and render them on the page.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
<StepHikeCompact.Code>
|
||||
|
||||
```ts app/countries/page.tsx
|
||||
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
|
||||
import { cookies } from "next/headers";
|
||||
```ts app/notes/page.tsx
|
||||
import { createClient } from '@/utils/supabase/server';
|
||||
|
||||
export default async function Index() {
|
||||
const cookieStore = cookies()
|
||||
const supabase = createServerComponentClient({ cookies: () => cookieStore })
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
const { data: countries } = await supabase.from("countries").select();
|
||||
export default async function Notes() {
|
||||
const supabase = createClient();
|
||||
const { data: notes } = await supabase.from("notes").select();
|
||||
|
||||
return (
|
||||
<ul className="my-auto text-foreground">
|
||||
{countries?.map((country) => (
|
||||
<li key={country.id}>{country.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
return <pre>{JSON.stringify(notes, null, 2)}</pre>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -117,7 +112,7 @@ export const meta = {
|
||||
<StepHikeCompact.Step step={5}>
|
||||
<StepHikeCompact.Details title="Start the app">
|
||||
|
||||
Run the development server, go to http://localhost:3000/countries in a browser and you should see the list of countries.
|
||||
Run the development server, go to http://localhost:3000/notes in a browser and you should see the list of notes.
|
||||
|
||||
</StepHikeCompact.Details>
|
||||
|
||||
|
||||
@@ -978,6 +978,22 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc
|
||||
>https://supabase.com/docs/guides/auth/server-side/overview</loc
|
||||
>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc
|
||||
>https://supabase.com/docs/guides/auth/server-side/creating-a-client</loc
|
||||
>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc
|
||||
>https://supabase.com/docs/guides/auth/server-side/email-based-auth-with-pkce-flow-for-ssr</loc
|
||||
@@ -992,6 +1008,14 @@
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc
|
||||
>https://supabase.com/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers</loc
|
||||
>
|
||||
<changefreq>weekly</changefreq>
|
||||
<changefreq>0.5</changefreq>
|
||||
</url>
|
||||
|
||||
<url>
|
||||
<loc>https://supabase.com/docs/guides/auth/concepts/redirect-urls</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
|
||||
Reference in new issue
Block a user