)}
{/* // parameters */}
diff --git a/apps/docs/docs/ref/csharp/release-notes.mdx b/apps/docs/docs/ref/csharp/release-notes.mdx
index 777621307dd..291636cfb08 100644
--- a/apps/docs/docs/ref/csharp/release-notes.mdx
+++ b/apps/docs/docs/ref/csharp/release-notes.mdx
@@ -3,6 +3,174 @@ id: release-notes
title: Release Notes
---
+## 0.11.0 - 2023-05-24
+
+- Update dependency: postgrest-csharp@3.2.0
+ - General codebase and QOL improvements. Exceptions are generally thrown through `PostgrestException` now instead
+ of `Exception`. A `FailureHint.Reason` is provided with failures if possible to parse.
+ - `AddDebugListener` is now available on the client to help with debugging
+ - Merges [#65](https://github.com/supabase-community/postgrest-csharp/pull/65) Cleanup + Add better exception handling
+ - Merges [#66](https://github.com/supabase-community/postgrest-csharp/pull/66) Local test Fixes
+ - Fixes [#67](https://github.com/supabase-community/postgrest-csharp/issues/67) Postgrest Reference attribute is
+ producing StackOverflow for circular references
+- Update dependency: gotrue-csharp@4.0.2
+ - [#58](https://github.com/supabase-community/gotrue-csharp/issues/58) - Add support for the `reauthentication` endpoint which allows for secure password changes.
+- Update dependency: realtime-csharp@6.0.1
+ - Updates publishing action for future packages, includes README and icon.
+ - Merges [#28](https://github.com/supabase-community/realtime-csharp/pull/28) and [#30](https://github.com/supabase-community/realtime-csharp/pull/30)
+ - The realtime client now takes a "fail-fast" approach. On establishing an initial connection, client will throw
+ a `RealtimeException` in `ConnectAsync()` if the socket server is unreachable. After an initial connection has been
+ established, the **client will continue attempting reconnections indefinitely until disconnected.**
+ - [Major, New] C# `EventHandlers` have been changed to `delegates`. This should allow for cleaner event data access over
+ the previous subclassed `EventArgs` setup. Events are scoped accordingly. For example, the `RealtimeSocket` error
+ handlers will receive events regarding socket connectivity; whereas the `RealtimeChannel` error handlers will receive
+ events according to `Channel` joining/leaving/etc. This is implemented with the following methods prefixed by (
+ Add/Remove/Clear):
+ - `RealtimeBroadcast.AddBroadcastEventHandler`
+ - `RealtimePresence.AddPresenceEventHandler`
+ - `RealtimeSocket.AddStateChangedHandler`
+ - `RealtimeSocket.AddMessageReceivedHandler`
+ - `RealtimeSocket.AddHeartbeatHandler`
+ - `RealtimeSocket.AddErrorHandler`
+ - `RealtimeClient.AddDebugHandler`
+ - `RealtimeClient.AddStateChangedHandler`
+ - `RealtimeChannel.AddPostgresChangeHandler`
+ - `RealtimeChannel.AddMessageReceivedHandler`
+ - `RealtimeChannel.AddErrorHandler`
+ - `Push.AddMessageReceivedHandler`
+ - [Major, new] `ClientOptions.Logger` has been removed in favor of `Client.AddDebugHandler()` which allows for
+ implementing custom logging solutions if desired.
+ - A simple logger can be set up with the following:
+ ```c#
+ client.AddDebugHandler((sender, message, exception) => Debug.WriteLine(message));
+ ```
+ - [Major] `Connect()` has been marked `Obsolete` in favor of `ConnectAsync()`
+ - Custom reconnection logic has been removed in favor of using the built-in logic from `Websocket.Client@4.6.1`.
+ - Exceptions that are handled within this library have been marked as `RealtimeException`s.
+ - The local, docker-composed test suite has been brought back (as opposed to remotely testing on live supabase servers)
+ to test against.
+ - Comments have been added throughout the entire codebase and an `XML` file is now generated on build.
+
+## 0.10.0 - 2023-05-14
+
+- Changes options to require `Supabase.SupabaseOptions.SessionPersistor` from using `ISupabaseSessionHandler`
+ to `IGotrueSessionPersistance` (these are now synchronous operations).
+- Update dependency: gotrue-csharp@4.0.1
+ - [#60](https://github.com/supabase-community/gotrue-csharp/pull/60) - Add interfaces, bug fixes, additional error
+ reason detection. Thanks [@wiverson](https://github.com/wiverson)!
+ - [#57](https://github.com/supabase-community/gotrue-csharp/pull/57) Refactor exceptions, code cleanup, and move to
+ delegate auth state changes
+ - Huge thank you to [@wiverson](https://github.com/wiverson) for his help on this refactor and release!
+ - Changes
+ - Exceptions have been simplified to a single `GotrueException`. A `Reason` field has been added
+ to `GotrueException` to clarify what happened. This should also be easier to manage as the Gotrue
+ server API & messages evolve.
+ - The session delegates for `Save`/`Load`/`Destroy` have been simplified to no longer require `async`.
+ - Console logging in a few places (most notable the background refresh thread) has been removed
+ in favor of a notification method. See `Client.AddDebugListener()` and the test cases for examples.
+ This will allow you to implement your own logging strategy (write to temp file, console, user visible
+ err console, etc).
+ - The client now more reliably emits AuthState changes.
+ - There is now a single source of truth for headers in the stateful Client - the `Options` headers.
+ - New feature:
+ - Added a `Settings` request to the stateless API only - you can now query the server instance to
+ determine if it's got the settings you need. This might allow for things like a visual
+ component in a tool to verify the GoTrue settings are working correctly, or tests that run differently
+ depending on the server configuration.
+ - Implementation notes:
+ - Test cases have been added to help ensure reliability of auth state change notifications
+ and persistence.
+ - Persistence is now managed via the same notifications as auth state change
+
+## 0.9.1 - 2023-04-28
+
+- Update dependency: gotrue-csharp@3.1.1
+ - Implements `SignInWithIdToken` for Apple/Google signing from LW7. A HUGE thank you
+ to [@wiverson](https://github.com/wiverson)!
+- Update dependency: realtime-csharp@5.0.5
+ - Re: [#27](https://github.com/supabase-community/realtime-csharp/issues/27) `PostgresChangesOptions` was not
+ setting `listenType` in constructor. Thanks [@Kuffs2205](https://github.com/Kuffs2205)
+- Update dependency: supabase-storage-csharp@1.2.10
+ - Re: [#7](https://github.com/supabase-community/storage-csharp/issues/7) Implements a `DownloadPublicFile` method.
+
+## 0.9.0 - 2023-04-12
+
+- Update dependency: gotrue-csharp@3.1.0
+
+ - [Minor] Implements PKCE auth flow. SignIn using a provider now returns an instance of `ProviderAuthState` rather
+ than a `string`.
+
+- Update dependency: supabase-storage-csharp@1.2.9
+ - Implements storage features from LW7:
+ - feat: custom file size limit and mime types at bucket
+ level [supabase/storage-js#151](https://github.com/supabase/storage-js/pull/151) file size and mime type
+ limits per bucket
+ - feat: quality option, image
+ transformation [supabase/storage-js#145](https://github.com/supabase/storage-js/pull/152) quality option for
+ image transformations
+ - feat: format option for webp
+ support [supabase/storage-js#142](https://github.com/supabase/storage-js/pull/142) format option for image
+ transformation
+
+## 0.8.8 - 2023-03-29
+
+- Update dependency: gotrue-csharp@3.0.6
+ - Supports adding `SignInOptions` (i.e. `RedirectTo`) on `OAuth Provider` SignIn requests.
+
+## 0.8.7 - 2023-03-23
+
+- Update dependency: realtime-csharp@5.0.4
+ - Re: [#26](https://github.com/supabase-community/realtime-csharp/pull/26) - Fixes Connect() not returning callback
+ result when the socket isn't null. Thanks [@BlueWaterCrystal](https://github.com/BlueWaterCrystal)!
+
+## 0.8.6 - 2023-03-23
+
+- Update dependency: supabase-storage-csharp@1.2.8
+ - [Merge #5](https://github.com/supabase-community/storage-csharp/pull/5) Added search string as an optional search
+ parameter. Thanks [@ElectroKnight22](https://github.com/ElectroKnight22)!
+
+## 0.8.5 - 2023-03-10
+
+- Update dependency: realtime-csharp@5.0.3
+ - Re: [#25](https://github.com/supabase-community/realtime-csharp/issues/25) - Support Channel being resubscribed
+ after having been unsubscribed, fixes rejoin timer being erroneously called on channel `Unsubscribe`.
+ Thanks [@Kuffs2205](https://github.com/Kuffs2205)!
+
+## 0.8.4 - 2023-03-03
+
+- Update dependency: supabase-storage-csharp@1.2.7
+ - Re: [#4](https://github.com/supabase-community/storage-csharp/issues/4) Implementation for `ClientOptions` which
+ supports specifying Upload, Download, and Request timeouts.
+- Update dependency: realtime-csharp@5.0.2
+ - Re: [#24](https://github.com/supabase-community/realtime-csharp/issues/24) - Fixes join failing until reconnect
+ happened + adds access token push on channel join. Big thank you to [@Honeyhead](https://github.com/honeyhead) for
+ the help debugging and identifying!
+
+## 0.8.3 - 2023-02-26
+
+- Update dependency: supabase-storage-csharp@1.2.5
+ - Provides fix
+ for [supabase-community/supabase-csharp#54](https://github.com/supabase-community/supabase-csharp/issues/54) -
+ Dynamic headers were always being overwritten by initialized token headers, so the storage client would not
+ receive user's access token as expected.
+ - Provides fix for upload progress not reporting
+ in [supabase-community/storage-csharp#3](https://github.com/supabase-community/storage-csharp/issues/3)
+- Update dependency: gotrue-csharp@3.0.5
+ - Fixes [#44](https://github.com/supabase-community/gotrue-csharp/issues/44) - refresh timer should automatically
+ reattempt (interval of 5s) for HTTP exceptions - gracefully exits on invalid refresh and triggers
+ an `AuthState.Changed` event
+
+## 0.8.2 - 2023-02-26
+
+- Update dependency: supabase-storage-csharp@1.2.4
+ - `UploadOrUpdate` now appropriately throws request exceptions
+
+## 0.8.1 - 2023-02-06
+
+- Update dependency: realtime-csharp@5.0.1
+ - Re: [#22](https://github.com/supabase-community/realtime-csharp/issues/22) - `SerializerSettings` were not being
+ passed to `PostgresChangesResponse` - Thanks [@Shenrak](https://github.com/Shenrak) for the help debugging!
+
## 0.8.0 - 2023-01-31
- Update dependency: realtime-csharp@5.0.0
diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx
index 6b24c000d4b..81b415e063c 100644
--- a/apps/docs/pages/_app.tsx
+++ b/apps/docs/pages/_app.tsx
@@ -1,3 +1,11 @@
+import '../../../packages/ui/build/css/themes/light.css'
+import '../../../packages/ui/build/css/themes/dark.css'
+
+import '../styles/ch.scss'
+import '../styles/main.scss?v=1.0.0'
+import '../styles/new-docs.scss'
+import '../styles/prism-okaidia.scss'
+
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { AuthProvider, ThemeProvider, useTelemetryProps } from 'common'
@@ -9,10 +17,6 @@ import Favicons from '~/components/Favicons'
import SiteLayout from '~/layouts/SiteLayout'
import { API_URL, IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants'
import { post } from '~/lib/fetchWrappers'
-import '../styles/ch.scss'
-import '../styles/main.scss?v=1.0.0'
-import '../styles/new-docs.scss'
-import '../styles/prism-okaidia.scss'
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter()
diff --git a/apps/docs/pages/guides/api/rest/client-libs.mdx b/apps/docs/pages/guides/api/rest/client-libs.mdx
index 5594ef78686..aa967a4b9eb 100644
--- a/apps/docs/pages/guides/api/rest/client-libs.mdx
+++ b/apps/docs/pages/guides/api/rest/client-libs.mdx
@@ -11,10 +11,10 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie
## Official Libraries
-| `Language` | `Source Code` | `Documentation` |
-| --------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- |
-| Javascript/Typescript | [supabase-js](https://github.com/supabase/supabase-js) | [Docs](https://supabase.com/docs/reference/javascript/introduction) |
-| Dart/Flutter | [supabase-dart](https://github.com/supabase/supabase-dart) | [Docs](https://supabase.com/docs/reference/dart/introduction) |
+| `Language` | `Source Code` | `Documentation` |
+| --------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
+| Javascript/Typescript | [supabase-js](https://github.com/supabase/supabase-js) | [Docs](https://supabase.com/docs/reference/javascript/introduction) |
+| Dart/Flutter | [supabase-flutter](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter) | [Docs](https://supabase.com/docs/reference/dart/introduction) |
## Community Libraries
@@ -22,10 +22,10 @@ Supabase provides client libraries for the REST and Realtime APIs. Some librarie
| ----------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| C# | [supabase-csharp](https://github.com/supabase-community/supabase-csharp) | [Docs](https://supabase.com/docs/reference/csharp/introduction) |
| Go | [supabase-go](https://github.com/supabase-community/supabase-go) | |
-| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | |
+| Kotlin | [supabase-kt](https://github.com/supabase-community/supabase-kt) | [Docs](https://supabase.com/docs/reference/kotlin/introduction) |
| Python | [supabase-py](https://github.com/supabase-community/supabase-py) | [Docs](https://supabase.com/docs/reference/python/initializing) |
| Ruby | [supabase-rb](https://github.com/supabase-community/supabase-rb) | |
-| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | |
+| Swift | [supabase-swift](https://github.com/supabase-community/supabase-swift) | [Docs](https://supabase.com/docs/reference/swift/introduction) |
| Godot Engine (GDScript) | [supabase-gdscript](https://github.com/supabase-community/godot-engine.supabase) | |
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx
new file mode 100644
index 00000000000..dd47ce758bd
--- /dev/null
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs-pages.mdx
@@ -0,0 +1,847 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'nextjs-pages',
+ title: 'Supabase Auth with Next.js Pages Directory',
+ description:
+ 'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.',
+ sidebar_label: 'Next.js (pages)',
+}
+
+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).
+
+## Install the Next.js helper library
+
+
+
+
+```sh
+npm install @supabase/auth-helpers-nextjs
+```
+
+This library supports the following tooling versions:
+
+- Node.js: `^10.13.0 || >=12.0.0`
+- Next.js: `>=10`
+
+Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
+
+```sh
+npm install @supabase/auth-helpers-react
+```
+
+
+
+
+```sh
+yarn add @supabase/auth-helpers-nextjs
+```
+
+This library supports the following tooling versions:
+
+- Node.js: `^10.13.0 || >=12.0.0`
+- Next.js: `>=10`
+
+Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
+
+```sh
+yarn add @supabase/auth-helpers-react
+```
+
+
+
+
+## Set up environment variables
+
+Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example).
+
+```bash title=.env.local
+NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
+NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
+```
+
+## Basic Setup
+
+
+
+
+Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
+
+```jsx title=pages/_app.js
+import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
+import { SessionContextProvider } from '@supabase/auth-helpers-react'
+import { useState } from 'react'
+
+function MyApp({ Component, pageProps }) {
+ // Create a new supabase browser client on every first render.
+ const [supabaseClient] = useState(() => createPagesBrowserClient())
+
+ return (
+
+
+
+ )
+}
+```
+
+
+
+
+Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
+
+```tsx lines=2,8 title=pages/_app.tsx
+import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
+import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
+import { useState } from 'react'
+
+function MyApp({
+ Component,
+ pageProps,
+}: AppProps<{
+ initialSession: Session
+}>) {
+ // Create a new supabase browser client on every first render.
+ const [supabaseClient] = useState(() => createPagesBrowserClient())
+
+ return (
+
+
+
+ )
+}
+```
+
+
+
+
+You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined.
+
+## Usage with TypeScript
+
+You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
+
+### Browser client
+
+Creating a new supabase client object:
+
+```tsx
+import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
+import { Database } from '../database.types'
+
+const supabaseClient = createPagesBrowserClient()
+```
+
+Retrieving a supabase client object from the SessionContext:
+
+```tsx
+import { useSupabaseClient } from '@supabase/auth-helpers-react'
+import { Database } from '../database.types'
+
+const supabaseClient = useSupabaseClient()
+```
+
+### Server client
+
+```tsx
+// Creating a new supabase server client object (e.g. in API route):
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+import type { NextApiRequest, NextApiResponse } from 'next'
+import type { Database } from 'types_db'
+
+export default async (req: NextApiRequest, res: NextApiResponse) => {
+ const supabaseServerClient = createPagesServerClient({
+ req,
+ res,
+ })
+ const {
+ data: { user },
+ } = await supabaseServerClient.auth.getUser()
+
+ res.status(200).json({ name: user?.name ?? '' })
+}
+```
+
+## Client-side data fetching with RLS
+
+For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
+
+```jsx lines=10-17
+import { Auth } from '@supabase/auth-ui-react'
+import { ThemeSupa } from '@supabase/auth-ui-shared'
+import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
+import { useEffect, useState } from 'react'
+
+const LoginPage = () => {
+ const supabaseClient = useSupabaseClient()
+ const user = useUser()
+ const [data, setData] = useState()
+
+ useEffect(() => {
+ async function loadData() {
+ const { data } = await supabaseClient.from('test').select('*')
+ setData(data)
+ }
+ // Only run query once user is logged in.
+ if (user) loadData()
+ }, [user])
+
+ if (!user)
+ return (
+
+ )
+
+ return (
+ <>
+
+
user:
+
{JSON.stringify(user, null, 2)}
+
client-side data fetching with RLS
+
{JSON.stringify(data, null, 2)}
+ >
+ )
+}
+
+export default LoginPage
+```
+
+## Server-side rendering (SSR)
+
+Create a server supabase client to retrieve the logged in user's session:
+
+```jsx title=pages/profile.js
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+export default function Profile({ user }) {
+ return
Hello {user.name}
+}
+
+export const getServerSideProps = async (ctx) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient(ctx)
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ }
+
+ return {
+ props: {
+ initialSession: session,
+ user: session.user,
+ },
+ }
+}
+```
+
+## Server-side data fetching with RLS
+
+You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
+
+
+
+
+```jsx
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+export default function ProtectedPage({ user, data }) {
+ return (
+ <>
+
Protected content for {user.email}
+
{JSON.stringify(data, null, 2)}
+
{JSON.stringify(user, null, 2)}
+ >
+ )
+}
+
+export const getServerSideProps = async (ctx) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient(ctx)
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ }
+
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('users').select('*')
+
+ return {
+ props: {
+ initialSession: session,
+ user: session.user,
+ data: data ?? [],
+ },
+ }
+}
+```
+
+
+
+
+```tsx
+import { User, createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+import { GetServerSidePropsContext } from 'next'
+
+export default function ProtectedPage({ user, data }: { user: User; data: any }) {
+ return (
+ <>
+
Protected content for {user.email}
+
{JSON.stringify(data, null, 2)}
+
{JSON.stringify(user, null, 2)}
+ >
+ )
+}
+
+export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient(ctx)
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ }
+
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('users').select('*')
+
+ return {
+ props: {
+ initialSession: session,
+ user: session.user,
+ data: data ?? [],
+ },
+ }
+}
+```
+
+
+
+
+## Server-side data fetching to OAuth APIs using `provider token` {`#oauth-provider-token`}
+
+When using third-party auth providers, sessions are initiated with an additional `provider_token` field which is persisted in the auth cookie and can be accessed within the session object. The `provider_token` can be used to make API requests to the OAuth provider's API endpoints on behalf of the logged-in user.
+
+
+
+
+```jsx
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+export default function ProtectedPage({ user, allRepos }) {
+ return (
+ <>
+
+ >
+ )
+}
+
+export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient(ctx)
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ }
+
+ // Retrieve provider_token & logged in user's third-party id from metadata
+ const { provider_token, user } = session
+ const userId = user.user_metadata.user_name
+
+ const allRepos = await (
+ await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
+ method: 'GET',
+ headers: {
+ Authorization: `token ${provider_token}`,
+ },
+ })
+ ).json()
+
+ return { props: { user, allRepos } }
+}
+```
+
+
+
+
+## Protecting API routes
+
+Create a server supabase client to retrieve the logged in user's session:
+
+
+
+
+```jsx title=pages/api/protected-route.js
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+const ProtectedRoute = async (req, res) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient({ req, res })
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return res.status(401).json({
+ error: 'not_authenticated',
+ description: 'The user does not have an active session or is not authenticated',
+ })
+
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('test').select('*')
+ res.json(data)
+}
+
+export default ProtectedRoute
+```
+
+
+
+
+```tsx title=pages/api/protected-route.ts
+import { NextApiHandler } from 'next'
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+const ProtectedRoute: NextApiHandler = async (req, res) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient({ req, res })
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return res.status(401).json({
+ error: 'not_authenticated',
+ description: 'The user does not have an active session or is not authenticated',
+ })
+
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('test').select('*')
+ res.json(data)
+}
+
+export default ProtectedRoute
+```
+
+
+
+
+## Auth with Next.js Middleware
+
+As an alternative to protecting individual pages you can use a [Next.js Middleware](https://nextjs.org/docs/middleware) to protect the entire directory or those that match the config object. In the following example, all requests to `/middleware-protected/*` will check whether a user is signed in, if successful the request will be forwarded to the destination route, otherwise the user will be redirected:
+
+```ts title=middleware.ts
+import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
+import { NextResponse } from 'next/server'
+import type { NextRequest } from 'next/server'
+
+export async function middleware(req: NextRequest) {
+ // We need to create a response and hand it to the supabase client to be able to modify the response headers.
+ const res = NextResponse.next()
+ // Create authenticated Supabase Client.
+ const supabase = createMiddlewareClient({ req, res })
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ // Check auth condition
+ if (session?.user.email?.endsWith('@gmail.com')) {
+ // Authentication successful, forward request to protected route.
+ return res
+ }
+
+ // Auth condition not met, redirect to home page.
+ const redirectUrl = req.nextUrl.clone()
+ redirectUrl.pathname = '/'
+ redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
+ return NextResponse.redirect(redirectUrl)
+}
+
+export const config = {
+ matcher: '/middleware-protected/:path*',
+}
+```
+
+## Migration Guide
+
+### Migrating to v0.5.X
+
+To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
+
+Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprecated and they will be removed in the next **minor** release (v0.6.X).
+
+Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
+
+#### `withApiAuth` deprecated!
+
+Use `createPagesServerClient` within your `NextApiHandler`:
+
+
+
+
+```tsx title=pages/api/protected-route.ts
+import { withApiAuth } from '@supabase/auth-helpers-nextjs'
+
+export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('test').select('*')
+ res.json(data)
+})
+```
+
+
+
+
+```tsx title=pages/api/protected-route.ts
+import { NextApiHandler } from 'next'
+import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
+
+const ProtectedRoute: NextApiHandler = async (req, res) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient({ req, res })
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return res.status(401).json({
+ error: 'not_authenticated',
+ description: 'The user does not have an active session or is not authenticated',
+ })
+
+ // Run queries with RLS on the server
+ const { data } = await supabase.from('test').select('*')
+ res.json(data)
+}
+
+export default ProtectedRoute
+```
+
+
+
+
+#### `withPageAuth` deprecated!
+
+Use `createPagesServerClient` within `getServerSideProps`:
+
+
+
+
+```tsx title=pages/profile.tsx
+import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
+
+export default function Profile({ user }: { user: User }) {
+ return
{JSON.stringify(user, null, 2)}
+}
+
+export const getServerSideProps = withPageAuth({ redirectTo: '/' })
+```
+
+
+
+
+```tsx title=pages/profile.js
+import { createPagesServerClient, User } from '@supabase/auth-helpers-nextjs'
+import { GetServerSidePropsContext } from 'next'
+
+export default function Profile({ user }: { user: User }) {
+ return
{JSON.stringify(user, null, 2)}
+}
+
+export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
+ // Create authenticated Supabase Client
+ const supabase = createPagesServerClient(ctx)
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ if (!session)
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ }
+
+ return {
+ props: {
+ initialSession: session,
+ user: session.user,
+ },
+ }
+}
+```
+
+
+
+
+#### `withMiddlewareAuth` deprecated!
+
+
+
+
+```tsx title=middleware.ts
+import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs'
+
+export const middleware = withMiddlewareAuth({
+ redirectTo: '/',
+ authGuard: {
+ isPermitted: async (user) => {
+ return user.email?.endsWith('@gmail.com') ?? false
+ },
+ redirectTo: '/insufficient-permissions',
+ },
+})
+
+export const config = {
+ matcher: '/middleware-protected',
+}
+```
+
+
+
+
+```tsx title=middleware.ts
+import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
+import { NextResponse } from 'next/server'
+import type { NextRequest } from 'next/server'
+
+export async function middleware(req: NextRequest) {
+ // We need to create a response and hand it to the supabase client to be able to modify the response headers.
+ const res = NextResponse.next()
+ // Create authenticated Supabase Client.
+ const supabase = createMiddlewareClient({ req, res })
+ // Check if we have a session
+ const {
+ data: { session },
+ } = await supabase.auth.getSession()
+
+ // Check auth condition
+ if (session?.user.email?.endsWith('@gmail.com')) {
+ // Authentication successful, forward request to protected route.
+ return res
+ }
+
+ // Auth condition not met, redirect to home page.
+ const redirectUrl = req.nextUrl.clone()
+ redirectUrl.pathname = '/'
+ redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
+ return NextResponse.redirect(redirectUrl)
+}
+
+export const config = {
+ matcher: '/middleware-protected',
+}
+```
+
+
+
+
+### Migrating to v0.4.X and supabase-js v2
+
+With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/v1/upgrade-guide) for the full set of changes within supabase-js.
+
+The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
+
+```jsx
+
+```
+
+The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createPagesBrowserClient` and `createPagesServerClient` methods. This allows you to provide the CLI-generated types to the client:
+
+```tsx
+// client-side
+import type { Database } from 'types_db'
+const [supabaseClient] = useState(() => createPagesBrowserClient())
+
+// server-side API route
+import type { NextApiRequest, NextApiResponse } from 'next'
+import type { Database } from 'types_db'
+
+export default async (req: NextApiRequest, res: NextApiResponse) => {
+ const supabaseServerClient = createPagesServerClient({
+ req,
+ res,
+ })
+ const {
+ data: { user },
+ } = await supabaseServerClient.auth.getUser()
+
+ res.status(200).json({ name: user?.name ?? '' })
+}
+```
+
+- The `UserProvider` has been replaced by the `SessionContextProvider`. Make sure to wrap your `pages/_app.js` componenent with the `SessionContextProvider`. Then, throughout your application you can use the `useSessionContext` hook to get the `session` and the `useSupabaseClient` hook to get an authenticated `supabaseClient`.
+- The `useUser` hook now returns the `user` object or `null`.
+- Usage with TypeScript: You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
+
+Creating a new supabase client object:
+
+```tsx
+import { Database } from '../database.types'
+
+const [supabaseClient] = useState(() => createPagesBrowserClient())
+```
+
+Retrieving a supabase client object from the SessionContext:
+
+```tsx
+import { useSupabaseClient } from '@supabase/auth-helpers-react'
+import { Database } from '../database.types'
+
+const supabaseClient = useSupabaseClient()
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx
deleted file mode 100644
index f16188a6bc8..00000000000
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs-server-components.mdx
+++ /dev/null
@@ -1,516 +0,0 @@
-import Layout from '~/layouts/DefaultGuideLayout'
-
-export const meta = {
- id: 'nextjs-server-components',
- title: 'Supabase Auth with Next.js app directory',
- description:
- 'Authentication helpers for creating an authenticated Supabase client in Next.js 13 app directory Server Components and Route Handlers.',
- sidebar_label: 'Next.js (app)',
-}
-
-The Next.js Auth Helpers package configures Supabase Auth to store the user's session in a cookie, rather than `localStorage`. This makes the users's session available server-side - in Server Components and Route Handlers - and is automatically sent along with any requests to Supabase.
-
-> Note: If you are using the `pages` directory, check out [Auth Helpers in Next.js](/docs/guides/auth/auth-helpers/nextjs).
-
-
-
-
-
-> To learn more about Supabase and the Next.js 13 app directory, check out [this playlist](https://youtube.com/playlist?list=PL5S4mPUpp4OtwG-qCxm8gA_hjaBq0OPdz).
-
-## Install the Next.js helper library
-
-
-
-
-
-```sh
-npm install @supabase/auth-helpers-nextjs
-```
-
-
-
-
-```sh
-yarn add @supabase/auth-helpers-nextjs
-```
-
-
-
-
-## Set up environment variables
-
-Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api) in the dashboard, and create a `.env.local` file with the following environment variables:
-
-```bash title=".env.local"
-NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
-NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
-```
-
-## Configure Middleware
-
-
-
-
-Middleware runs immediately before each route in rendered. Next.js only provides read access to headers and cookies in Server Components and Route Handlers, however, Supabase needs to be able to set cookies and headers to refresh expired access tokens. Therefore, you must call the `getSession` function in `middleware.js` in order to use a Supabase client in Server Components or Route Handlers.
-
-Create a new `middleware.js` file in the root of your project and populate with the following:
-
-```jsx title="middleware.js"
-import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { NextResponse } from 'next/server'
-
-export async function middleware(req) {
- const res = NextResponse.next()
- const supabase = createMiddlewareSupabaseClient({ req, res })
- await supabase.auth.getSession()
- return res
-}
-```
-
-
-
-
-
-Middleware runs immediately before each route in rendered. Next.js only provides read access to headers and cookies in Server Components and Route Handlers, however, Supabase needs to be able to set cookies and headers to refresh expired access tokens. Therefore, you must call the `getSession` function in `middleware.ts` in order to use a Supabase client in Server Components or Route Handlers.
-
-Create a new `middleware.ts` file in the root of your project and populate with the following:
-
-```tsx title="middleware.ts"
-import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { NextResponse } from 'next/server'
-
-import type { NextRequest } from 'next/server'
-import type { Database } from '@/lib/database.types'
-
-export async function middleware(req: NextRequest) {
- const res = NextResponse.next()
- const supabase = createMiddlewareSupabaseClient({ req, res })
- await supabase.auth.getSession()
- return res
-}
-```
-
-> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createMiddlewareSupabaseClient` to add type support to the Supabase client.
-
-
-
-
-## Supabase Provider
-
-All Client Components need to share a single instance of the Supabase client. We can wrap our application in a `` and use React Context to create a global Supabase instance.
-
-
-
-
-Create a new file at `/app/supabase-provider.jsx` and populate with the following:
-
-```jsx title="app/supabase-provider.jsx"
-'use client'
-
-import { createContext, useContext, useEffect, useState } from 'react'
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { useRouter } from 'next/navigation'
-
-const Context = createContext(undefined)
-
-export default function SupabaseProvider({ children, session }) {
- const [supabase] = useState(() => createBrowserSupabaseClient())
- const router = useRouter()
-
- useEffect(() => {
- const {
- data: { subscription },
- } = supabase.auth.onAuthStateChange(() => {
- router.refresh()
- })
-
- return () => {
- subscription.unsubscribe()
- }
- }, [router, supabase])
-
- return (
-
- <>{children}>
-
- )
-}
-
-export const useSupabase = () => {
- const context = useContext(Context)
-
- if (context === undefined) {
- throw new Error('useSupabase must be used inside SupabaseProvider')
- }
-
- return context
-}
-```
-
-
-
-
-
-Create a new file at `/app/supabase-provider.tsx` and populate with the following:
-
-```tsx title="app/supabase-provider.tsx"
-'use client'
-
-import { createContext, useContext, useEffect, useState } from 'react'
-import { Session, createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { useRouter } from 'next/navigation'
-
-import type { SupabaseClient } from '@supabase/auth-helpers-nextjs'
-import type { Database } from '@/lib/database.types'
-
-type MaybeSession = Session | null
-
-type SupabaseContext = {
- supabase: SupabaseClient
- session: MaybeSession
-}
-
-const Context = createContext(undefined)
-
-export default function SupabaseProvider({
- children,
- session,
-}: {
- children: React.ReactNode
- session: MaybeSession
-}) {
- const [supabase] = useState(() => createBrowserSupabaseClient())
- const router = useRouter()
-
- useEffect(() => {
- const {
- data: { subscription },
- } = supabase.auth.onAuthStateChange(() => {
- router.refresh()
- })
-
- return () => {
- subscription.unsubscribe()
- }
- }, [router, supabase])
-
- return (
-
- <>{children}>
-
- )
-}
-
-export const useSupabase = () => {
- const context = useContext(Context)
-
- if (context === undefined) {
- throw new Error('useSupabase must be used inside SupabaseProvider')
- }
-
- return context
-}
-```
-
-> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createBrowserSupabaseClient` to add type support to the Supabase client.
-
-
-
-
-
-
-
-Modify `layout.jsx` to wrap the application with the `` component:
-
-```jsx title="app/layout.jsx"
-import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { headers, cookies } from 'next/headers'
-import './globals.css'
-import SupabaseProvider from './supabase-provider'
-
-export const metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
-}
-
-export default async function RootLayout({ children }) {
- const supabase = createServerComponentSupabaseClient({
- headers,
- cookies,
- })
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
-
-
- {children}
-
-
- )
-}
-```
-
-
-
-
-
-Modify `layout.tsx` to wrap the application with the `` component:
-
-```tsx title="app/layout.tsx"
-import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { headers, cookies } from 'next/headers'
-import './globals.css'
-import SupabaseProvider from './supabase-provider'
-
-export const metadata = {
- title: 'Create Next App',
- description: 'Generated by create next app',
-}
-
-export default async function RootLayout({ children }: { children: React.ReactNode }) {
- const supabase = createServerComponentSupabaseClient({
- headers,
- cookies,
- })
-
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- return (
-
-
- {children}
-
-
- )
-}
-```
-
-
-
-
-Now any of our Client Components can use the `useSupabase` hook to ensure they are using the same instance of a Supabase client.
-
-## Creating a Supabase Client
-
-### Client Components
-
-While Server Components are great for data fetching, we still need to use Supabase client-side for [authentication](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login.tsx) and [realtime subscriptions](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
-
-As mentioned above, it is important that all Client Components share a single instance of the Supabase client. We can use the `useSupabase` hook we created above to ensure this is the case.
-
-
-
-
-```jsx title="app/new-post.jsx"
-'use client'
-
-import { useState } from 'react'
-import { useSupabase } from './supabase-provider'
-
-export default function NewPost() {
- const [content, setContent] = useState('')
- const { supabase } = useSupabase()
-
- const handleSave = async () => {
- const { data } = await supabase.from('posts').insert({ content }).select()
- }
-
- return (
- <>
- setContent(e.target.value)} value={content} />
-
- >
- )
-}
-```
-
-
-
-
-
-```jsx title="app/new-post.tsx"
-'use client'
-
-import { useState } from 'react'
-import { useSupabase } from './supabase-provider'
-
-export default function NewPost() {
- const [content, setContent] = useState('')
- const { supabase } = useSupabase()
-
- const handleSave = async () => {
- const { data } = await supabase.from('posts').insert({ content }).select()
- }
-
- return (
- <>
- setContent(e.target.value)} value={content} />
-
- >
- )
-}
-```
-
-
-
-
-> check out [this example](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/supabase-provider.tsx) for making the user's session available to all Client Components.
-
-### Server Components
-
-In order to use Supabase in Server Components, you need to have implemented the `middleware.ts` steps above 👆
-
-
-
-
-```jsx title="app/page.jsx"
-import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { headers, cookies } from 'next/headers'
-
-// do not cache this page
-export const revalidate = 0
-
-export default async function ServerComponent() {
- const supabase = createServerComponentSupabaseClient({
- headers,
- cookies,
- })
- const { data } = await supabase.from('posts').select('*')
-
- return
{JSON.stringify(data, null, 2)}
-}
-```
-
-
-
-
-
-```tsx title="app/page.tsx"
-import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { headers, cookies } from 'next/headers'
-
-import type { Database } from '@/lib/database.types'
-
-// do not cache this page
-export const revalidate = 0
-
-export default async function ServerComponent() {
- const supabase = createServerComponentSupabaseClient({
- headers,
- cookies,
- })
- const { data } = await supabase.from('posts').select('*')
-
- return
{JSON.stringify(data, null, 2)}
-}
-```
-
-
-
-
-> check out [this example](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/page.tsx) for redirecting unauthenticated users - protected pages.
-
-### Route Handlers
-
-In order to use Supabase in Route Handlers, you need to have implemented the `middleware.ts` steps above 👆
-
-
-
-
-```jsx title="app/api/posts/route.jsx"
-import { createRouteHandlerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { NextResponse } from 'next/server'
-import { headers, cookies } from 'next/headers'
-
-// do not cache this page
-export const revalidate = 0
-
-export async function GET() {
- const supabase = createRouteHandlerSupabaseClient({
- headers,
- cookies,
- })
- const { data } = await supabase.from('posts').select('*')
- return NextResponse.json(data)
-}
-```
-
-
-
-
-
-```tsx title="app/api/posts/route.tsx"
-import { createRouteHandlerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { NextResponse } from 'next/server'
-import { headers, cookies } from 'next/headers'
-
-import type { Database } from '@/lib/database.types'
-
-// do not cache this page
-export const revalidate = 0
-
-export async function GET() {
- const supabase = createRouteHandlerSupabaseClient({
- headers,
- cookies,
- })
- const { data } = await supabase.from('posts').select('*')
-
- return NextResponse.json(data)
-}
-```
-
-
-
-
-> Check out [this repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for a full example including [authentication](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login.tsx), [realtime](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx) and [protected pages](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/page.tsx).
-
-export const Page = ({ children }) =>
-
-export default Page
diff --git a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
index 2a5cb8a96f4..8131d278ab3 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/nextjs.mdx
@@ -2,14 +2,19 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'nextjs',
- title: 'Supabase Auth with Next.js',
- description: 'Authentication helpers for Next.js API routes, middleware, and SSR.',
- sidebar_label: 'Next.js (pages)',
+ title: 'Supabase Auth with the Next.js App Router',
+ description:
+ 'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.',
+ sidebar_label: 'Next.js',
}
-This submodule provides convenience helpers for implementing user authentication in Next.js applications.
+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.
-## Install the Next.js helper library
+> Note: If you are using the `pages` directory, check out [Auth Helpers in Next.js Pages Directory](/docs/guides/auth/auth-helpers/nextjs-pages).
+
+## Configuration
+
+### Install Next.js Auth Helpers library
+
```sh
npm install @supabase/auth-helpers-nextjs
```
-This library supports the following tooling versions:
-
-- Node.js: `^10.13.0 || >=12.0.0`
-- Next.js: `>=10`
-
-> Note: As of [Next.js 13.4](https://nextjs.org/blog/next-13-4), the `app` directory and Server Components have reached stable status. Check out our guide on using [Auth Helpers with Next.js Server Components](/docs/guides/auth/auth-helpers/nextjs-server-components).
-
-Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
-
-```sh
-npm install @supabase/auth-helpers-react
-```
-
@@ -43,32 +36,21 @@ npm install @supabase/auth-helpers-react
yarn add @supabase/auth-helpers-nextjs
```
-This library supports the following tooling versions:
-
-- Node.js: `^10.13.0 || >=12.0.0`
-- Next.js: `>=10`
-
-> Note: Next.js 13 is stable, however, the new `app` directory and Server Components are still in beta. Check out our experimental guide on [using Auth Helpers with Next.js Server Components](/docs/guides/auth/auth-helpers/nextjs-server-components).
-
-Additionally, install the **React Auth Helpers** for components and hooks that can be used across all React-based frameworks.
-
-```sh
-yarn add @supabase/auth-helpers-react
-```
-
-## Set up environment variables
+### Declare Environment Variables
-Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example).
+Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api), and create a `.env.local` file with the following environment variables:
-```bash title=.env.local
-NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
-NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
+```bash title=".env.local"
+NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
+NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
```
-## Basic Setup
+### Refresh session with Middleware
+
+[Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) runs immediately before each route in rendered. Next.js only provides read access to cookies in Server Components, therefore, Middleware is used to refresh the user's session before loading Server Component routes.
-Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
+Create a new `middleware.js` file in the root of your project and populate with the following:
-```jsx title=pages/_app.js
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { SessionContextProvider } from '@supabase/auth-helpers-react'
-import { useState } from 'react'
+```jsx title="middleware.js"
+import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
+import { NextResponse } from 'next/server'
-function MyApp({ Component, pageProps }) {
- // Create a new supabase browser client on every first render.
- const [supabaseClient] = useState(() => createBrowserSupabaseClient())
-
- return (
-
-
-
- )
+export async function middleware(req) {
+ const res = NextResponse.next()
+ const supabase = createMiddlewareClient({ req, res })
+ await supabase.auth.getSession()
+ return res
}
```
+
-Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
+Create a new `middleware.ts` file in the root of your project and populate with the following:
-```tsx lines=2,8 title=pages/_app.tsx
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
-import { useState } from 'react'
+```tsx title="middleware.ts"
+import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
+import { NextResponse } from 'next/server'
-function MyApp({
- Component,
- pageProps,
-}: AppProps<{
- initialSession: Session
-}>) {
- // Create a new supabase browser client on every first render.
- const [supabaseClient] = useState(() => createBrowserSupabaseClient())
+import type { NextRequest } from 'next/server'
+import type { Database } from '@/lib/database.types'
- return (
-
-
-
- )
+export async function middleware(req: NextRequest) {
+ const res = NextResponse.next()
+ const supabase = createMiddlewareClient({ req, res })
+ await supabase.auth.getSession()
+ return res
}
```
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createMiddlewareClient` to add type support to the Supabase client.
+
-You can now determine if a user is authenticated by checking that the `user` object returned by the `useUser()` hook is defined.
+> The `getSession` function must be called for any Server Component routes that use a Supabase client.
-## Usage with TypeScript
+### Code Exchange Route
-You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
+The `Code Exchange` route is required for the [server-side auth flow](https://supabase.com/docs/guides/auth/server-side-rendering) implemented by the Next.js Auth Helpers. It exchanges an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.
-### Browser client
+
+
-Creating a new supabase client object:
+Create a new file at `app/auth/callback/route.js` and populate with the following:
-```tsx
-import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { Database } from '../database.types'
+```jsx title="app/auth/callback.route.js"
+import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
+import { cookies } from 'next/headers'
+import { NextResponse } from 'next/server'
-const supabaseClient = createBrowserSupabaseClient()
-```
+export async function GET(request) {
+ const requestUrl = new URL(request.url)
+ const code = requestUrl.searchParams.get('code')
-Retrieving a supabase client object from the SessionContext:
+ if (code) {
+ const supabase = createRouteHandlerClient({ cookies })
+ await supabase.auth.exchangeCodeForSession(code)
+ }
-```tsx
-import { useSupabaseClient } from '@supabase/auth-helpers-react'
-import { Database } from '../database.types'
-
-const supabaseClient = useSupabaseClient()
-```
-
-### Server client
-
-```tsx
-// Creating a new supabase server client object (e.g. in API route):
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import type { NextApiRequest, NextApiResponse } from 'next'
-import type { Database } from 'types_db'
-
-export default async (req: NextApiRequest, res: NextApiResponse) => {
- const supabaseServerClient = createServerSupabaseClient({
- req,
- res,
- })
- const {
- data: { user },
- } = await supabaseServerClient.auth.getUser()
-
- res.status(200).json({ name: user?.name ?? '' })
+ // URL to redirect to after sign in process completes
+ return NextResponse.redirect(requestUrl.origin)
}
```
-## Client-side data fetching with RLS
+
-For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
+
-```jsx lines=10-17
-import { Auth } from '@supabase/auth-ui-react'
-import { ThemeSupa } from '@supabase/auth-ui-shared'
-import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
+Create a new file at `app/auth/callback/route.ts` and populate with the following:
+
+```tsx title="app/auth/callback.route.ts"
+import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
+import { cookies } from 'next/headers'
+import { NextResponse } from 'next/server'
+
+import type { NextRequest } from 'next/server'
+import type { Database } from '@/lib/database.types'
+
+export async function GET(request: NextRequest) {
+ const requestUrl = new URL(request.url)
+ const code = requestUrl.searchParams.get('code')
+
+ if (code) {
+ const supabase = createRouteHandlerClient({ cookies })
+ await supabase.auth.exchangeCodeForSession(code)
+ }
+
+ // URL to redirect to after sign in process completes
+ return NextResponse.redirect(requestUrl.origin)
+}
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
+
+
+
+
+## Authentication
+
+Authentication can be initiated [client](/docs/guides/auth/auth-helpers/nextjs#client-side) or [server-side](/docs/guides/auth/auth-helpers/nextjs#server-side). All of the [supabase-js authentication strategies](http://localhost:3001/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.
+
+> Note: The authentication flow requires the [Code Exchange Route](/docs/guides/auth/auth-helpers/nextjs#code-exchange-route) to exchange a `code` for the user's `session`.
+
+### Client-side
+
+Client Components can be used to trigger the authentication process from event handlers.
+
+
+
+
+```jsx title="app/login.js"
+'use client'
+
+import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
+import { useRouter } from 'next/navigation'
+import { useState } from 'react'
+
+export default function Login() {
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const router = useRouter()
+ const supabase = createClientComponentClient()
+
+ const handleSignUp = async () => {
+ await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ emailRedirectTo: `${location.origin}/auth/callback`,
+ },
+ })
+ router.refresh()
+ }
+
+ const handleSignIn = async () => {
+ await supabase.auth.signInWithPassword({
+ email,
+ password,
+ })
+ router.refresh()
+ }
+
+ const handleSignOut = async () => {
+ await supabase.auth.signOut()
+ router.refresh()
+ }
+
+ return (
+ <>
+ setEmail(e.target.value)} value={email} />
+ setPassword(e.target.value)}
+ value={password}
+ />
+
+
+
+ >
+ )
+}
+```
+
+
+
+
+
+```tsx title="app/login.ts"
+'use client'
+
+import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
+import { useRouter } from 'next/navigation'
+import { useState } from 'react'
+
+import type { Database } from '@/lib/database.types'
+
+export default function Login() {
+ const [email, setEmail] = useState('')
+ const [password, setPassword] = useState('')
+ const router = useRouter()
+ const supabase = createClientComponentClient()
+
+ const handleSignUp = async () => {
+ await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ emailRedirectTo: `${location.origin}/auth/callback`,
+ },
+ })
+ router.refresh()
+ }
+
+ const handleSignIn = async () => {
+ await supabase.auth.signInWithPassword({
+ email,
+ password,
+ })
+ router.refresh()
+ }
+
+ const handleSignOut = async () => {
+ await supabase.auth.signOut()
+ router.refresh()
+ }
+
+ return (
+ <>
+ setEmail(e.target.value)} value={email} />
+ setPassword(e.target.value)}
+ value={password}
+ />
+
+
+
+ >
+ )
+}
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createClientComponentClient` to add type support to the Supabase client.
+
+
+
+
+### Server-side
+
+The combination of [Server Components](https://nextjs.org/docs/getting-started/react-essentials#server-components) and [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions) can be used to trigger the authentication process from form submissions.
+
+> Note: [Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions) are currently in Alpha and likely to change. We recommend [triggering the authentication flow client-side](/docs/guides/auth/auth-helpers/nextjs#client-side) for production applications.
+
+
+
+
+```jsx title="app/login.js"
+import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
+import { revalidatePath } from 'next/cache'
+import { cookies } from 'next/headers'
+
+export default async function Login() {
+ const handleSignUp = async (formData) => {
+ 'use server'
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ emailRedirectTo: 'http://localhost:3000/auth/callback',
+ },
+ })
+
+ revalidatePath('/')
+ }
+
+ const handleSignIn = async (formData) => {
+ 'use server'
+ const email = formData.get('email')
+ const password = formData.get('password')
+
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signInWithPassword({
+ email,
+ password,
+ })
+
+ revalidatePath('/')
+ }
+
+ const handleSignOut = async () => {
+ 'use server'
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signOut()
+ revalidatePath('/')
+ }
+
+ return (
+
+ )
+}
+```
+
+
+
+
+
+```tsx title="app/login.ts"
+import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
+import { revalidatePath } from 'next/cache'
+import { cookies } from 'next/headers'
+
+import type { Database } from '@/lib/database.types'
+
+export default async function Login() {
+ const handleSignUp = async (formData: FormData) => {
+ 'use server'
+ const email = String(formData.get('email'))
+ const password = String(formData.get('password'))
+
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ emailRedirectTo: 'http://localhost:3000/auth/callback',
+ },
+ })
+
+ revalidatePath('/')
+ }
+
+ const handleSignIn = async (formData: FormData) => {
+ 'use server'
+ const email = String(formData.get('email'))
+ const password = String(formData.get('password'))
+
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signInWithPassword({
+ email,
+ password,
+ })
+
+ revalidatePath('/')
+ }
+
+ const handleSignOut = async () => {
+ 'use server'
+ const supabase = createServerActionClient({ cookies })
+ await supabase.auth.signOut()
+ revalidatePath('/')
+ }
+
+ return (
+
+ )
+}
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerActionClient` to add type support to the Supabase client.
+
+
+
+
+## Creating a Supabase Client
+
+### Client Component
+
+[Client Components](https://nextjs.org/docs/getting-started/react-essentials#client-components) allow the use of client-side hooks - such as `useEffect` and `useState`. They can be used to request data from Supabase client-side, and [subscribe to realtime events](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
+
+
+
+
+```jsx title="app/client/page.jsx"
+'use client'
+
+import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
import { useEffect, useState } from 'react'
-const LoginPage = () => {
- const supabaseClient = useSupabaseClient()
- const user = useUser()
- const [data, setData] = useState()
+export default function Home() {
+ const [todos, setTodos] = useState()
+ const supabase = createClientComponentClient()
useEffect(() => {
- async function loadData() {
- const { data } = await supabaseClient.from('test').select('*')
- setData(data)
- }
- // Only run query once user is logged in.
- if (user) loadData()
- }, [user])
-
- if (!user)
- return (
-
- )
-
- return (
- <>
-
-
user:
-
{JSON.stringify(user, null, 2)}
-
client-side data fetching with RLS
-
{JSON.stringify(data, null, 2)}
- >
- )
-}
-
-export default LoginPage
-```
-
-## Server-side rendering (SSR)
-
-Create a server supabase client to retrieve the logged in user's session:
-
-```jsx title=pages/profile.js
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-
-export default function Profile({ user }) {
- return
}
```
-## Server-side data fetching with RLS
+
-You can use the server supabase client to run [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) authenticated queries server-side:
+
+
+```jsx title="app/new-post.tsx"
+"use client";
+
+import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
+import { useEffect, useState } from "react";
+
+import type { Database } from '@/lib/database.types'
+
+type Todo = Database["public"]["Tables"]["todos"]["Row"];
+
+export default function Home() {
+ const [todos, setTodos] = useState(null);
+ const supabase = createClientComponentClient();
+
+ useEffect(() => {
+ const getData = async () => {
+ const { data } = await supabase.from("todos").select();
+ setTodos(data);
+ };
+
+ getData();
+ }, []);
+
+ return todos ? (
+
{JSON.stringify(todos, null, 2)}
+ ) : (
+
Loading todos...
+ );
+}
+```
+
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createClientComponentClient` to add type support to the Supabase client.
+
+
+
+
+> check out [this repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for more examples, including [realtime subscriptions](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx).
+
+### Server Component
+
+[Server Components](https://nextjs.org/docs/getting-started/react-essentials#server-components) allow for asynchronous data to be fetched server-side.
+
+> Note: In order to use Supabase in Server Components, you need to have implemented the [Middleware](/docs/guides/auth/auth-helpers/nextjs#refresh-session-with-middleware) steps above.
-```jsx
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
+```jsx title="app/page.jsx"
+import { cookies } from 'next/headers'
+import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
-export default function ProtectedPage({ user, data }) {
- return (
- <>
-
Protected content for {user.email}
-
{JSON.stringify(data, null, 2)}
-
{JSON.stringify(user, null, 2)}
- >
- )
-}
-
-export const getServerSideProps = async (ctx) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient(ctx)
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return {
- redirect: {
- destination: '/',
- permanent: false,
- },
- }
-
- // Run queries with RLS on the server
- const { data } = await supabase.from('users').select('*')
-
- return {
- props: {
- initialSession: session,
- user: session.user,
- data: data ?? [],
- },
- }
+export default async function Home() {
+ const supabase = createServerComponentClient({ cookies })
+ const { data } = await supabase.from('todos').select()
+ return
{JSON.stringify(data, null, 2)}
}
```
+
-```tsx
-import { User, createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-import { GetServerSidePropsContext } from 'next'
+```tsx title="app/page.tsx"
+import { cookies } from 'next/headers'
+import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
-export default function ProtectedPage({ user, data }: { user: User; data: any }) {
- return (
- <>
-
Protected content for {user.email}
-
{JSON.stringify(data, null, 2)}
-
{JSON.stringify(user, null, 2)}
- >
- )
-}
+import type { Database } from '@/lib/database.types'
-export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient(ctx)
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return {
- redirect: {
- destination: '/',
- permanent: false,
- },
- }
-
- // Run queries with RLS on the server
- const { data } = await supabase.from('users').select('*')
-
- return {
- props: {
- initialSession: session,
- user: session.user,
- data: data ?? [],
- },
- }
+export default async function ServerComponent() {
+ const supabase = createServerComponentClient({ cookies })
+ const { data } = await supabase.from('todos').select()
+ return
{JSON.stringify(data, null, 2)}
}
```
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerComponentClient` to add type support to the Supabase client.
+
-## Server-side data fetching to OAuth APIs using `provider token` {`#oauth-provider-token`}
+> check out [this repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) for more examples, including redirecting unauthenticated users - [protected pages](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/[id]/page.tsx).
-When using third-party auth providers, sessions are initiated with an additional `provider_token` field which is persisted in the auth cookie and can be accessed within the session object. The `provider_token` can be used to make API requests to the OAuth provider's API endpoints on behalf of the logged-in user.
+### Server Action
+
+[Server Actions](https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions) allow mutations to be performed server-side.
+
+> Note: Server Actions are currently in `alpha` so may change without notice.
-```jsx
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
+```jsx title="app/new-post.jsx"
+import { cookies } from 'next/headers'
+import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
+import { revalidatePath } from 'next/cache'
+
+export default async function NewTodo() {
+ const addTodo = async (formData) => {
+ 'use server'
+
+ const title = formData.get('title')
+ const supabase = createServerActionClient({ cookies })
+ await supabase.from('todos').insert({ title })
+ revalidatePath('/')
+ }
-export default function ProtectedPage({ user, allRepos }) {
return (
- <>
-
- >
+
)
}
-
-export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient(ctx)
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return {
- redirect: {
- destination: '/',
- permanent: false,
- },
- }
-
- // Retrieve provider_token & logged in user's third-party id from metadata
- const { provider_token, user } = session
- const userId = user.user_metadata.user_name
-
- const allRepos = await (
- await fetch(`https://api.github.com/search/repositories?q=user:${userId}`, {
- method: 'GET',
- headers: {
- Authorization: `token ${provider_token}`,
- },
- })
- ).json()
-
- return { props: { user, allRepos } }
-}
```
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerActionClient` to add type support to the Supabase client.
+
-## Protecting API routes
+### Route Handler
-Create a server supabase client to retrieve the logged in user's session:
+[Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/router-handlers) replace API Routes and allow for logic to be performed server-side. They can respond to `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS` requests.
-```jsx title=pages/api/protected-route.js
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-
-const ProtectedRoute = async (req, res) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient({ req, res })
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return res.status(401).json({
- error: 'not_authenticated',
- description: 'The user does not have an active session or is not authenticated',
- })
-
- // Run queries with RLS on the server
- const { data } = await supabase.from('test').select('*')
- res.json(data)
-}
-
-export default ProtectedRoute
-```
-
-
-
-
-```tsx title=pages/api/protected-route.ts
-import { NextApiHandler } from 'next'
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-
-const ProtectedRoute: NextApiHandler = async (req, res) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient({ req, res })
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return res.status(401).json({
- error: 'not_authenticated',
- description: 'The user does not have an active session or is not authenticated',
- })
-
- // Run queries with RLS on the server
- const { data } = await supabase.from('test').select('*')
- res.json(data)
-}
-
-export default ProtectedRoute
-```
-
-
-
-
-## Auth with Next.js Middleware
-
-As an alternative to protecting individual pages you can use a [Next.js Middleware](https://nextjs.org/docs/middleware) to protect the entire directory or those that match the config object. In the following example, all requests to `/middleware-protected/*` will check whether a user is signed in, if successful the request will be forwarded to the destination route, otherwise the user will be redirected:
-
-```ts title=middleware.ts
-import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
+```jsx title="app/api/todos/route.jsx"
+import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
-import type { NextRequest } from 'next/server'
+import { cookies } from 'next/headers'
-export async function middleware(req: NextRequest) {
- // We need to create a response and hand it to the supabase client to be able to modify the response headers.
- const res = NextResponse.next()
- // Create authenticated Supabase Client.
- const supabase = createMiddlewareSupabaseClient({ req, res })
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- // Check auth condition
- if (session?.user.email?.endsWith('@gmail.com')) {
- // Authentication successful, forward request to protected route.
- return res
- }
-
- // Auth condition not met, redirect to home page.
- const redirectUrl = req.nextUrl.clone()
- redirectUrl.pathname = '/'
- redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
- return NextResponse.redirect(redirectUrl)
-}
-
-export const config = {
- matcher: '/middleware-protected/:path*',
-}
-```
-
-## Migration Guide
-
-### Migrating to v0.5.X
-
-To make these helpers more flexible as well as more maintainable and easier to upgrade for new versions of Next.js, we're stripping them down to the most useful part which is managing the cookies and giving you an authenticated supabase-js client in any environment (client, server, middleware/edge).
-
-Therefore we're marking the `withApiAuth`, `withPageAuth`, and `withMiddlewareAuth` higher order functions as deprecated and they will be removed in the next **minor** release (v0.6.X).
-
-Please follow the steps below to update your API routes, pages, and middleware handlers. Thanks!
-
-#### `withApiAuth` deprecated!
-
-Use `createServerSupabaseClient` within your `NextApiHandler`:
-
-
-
-
-```tsx title=pages/api/protected-route.ts
-import { withApiAuth } from '@supabase/auth-helpers-nextjs'
-
-export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
- // Run queries with RLS on the server
- const { data } = await supabase.from('test').select('*')
- res.json(data)
-})
-```
-
-
-
-
-```tsx title=pages/api/protected-route.ts
-import { NextApiHandler } from 'next'
-import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
-
-const ProtectedRoute: NextApiHandler = async (req, res) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient({ req, res })
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return res.status(401).json({
- error: 'not_authenticated',
- description: 'The user does not have an active session or is not authenticated',
- })
-
- // Run queries with RLS on the server
- const { data } = await supabase.from('test').select('*')
- res.json(data)
-}
-
-export default ProtectedRoute
-```
-
-
-
-
-#### `withPageAuth` deprecated!
-
-Use `createServerSupabaseClient` within `getServerSideProps`:
-
-
-
-
-```tsx title=pages/profile.tsx
-import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
-
-export default function Profile({ user }: { user: User }) {
- return
{JSON.stringify(user, null, 2)}
-}
-
-export const getServerSideProps = withPageAuth({ redirectTo: '/' })
-```
-
-
-
-
-```tsx title=pages/profile.js
-import { createServerSupabaseClient, User } from '@supabase/auth-helpers-nextjs'
-import { GetServerSidePropsContext } from 'next'
-
-export default function Profile({ user }: { user: User }) {
- return
{JSON.stringify(user, null, 2)}
-}
-
-export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
- // Create authenticated Supabase Client
- const supabase = createServerSupabaseClient(ctx)
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
-
- if (!session)
- return {
- redirect: {
- destination: '/',
- permanent: false,
- },
- }
-
- return {
- props: {
- initialSession: session,
- user: session.user,
- },
- }
+export async function POST(request) {
+ const { title } = await request.json()
+ const supabase = createRouteHandlerClient({ cookies })
+ const { data } = await supabase.from('todos').insert({ title }).select()
+ return NextResponse.json(data)
}
```
-
-#### `withMiddlewareAuth` deprecated!
+
-
-
-
-```tsx title=middleware.ts
-import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs'
-
-export const middleware = withMiddlewareAuth({
- redirectTo: '/',
- authGuard: {
- isPermitted: async (user) => {
- return user.email?.endsWith('@gmail.com') ?? false
- },
- redirectTo: '/insufficient-permissions',
- },
-})
-
-export const config = {
- matcher: '/middleware-protected',
-}
-```
-
-
-
-
-```tsx title=middleware.ts
-import { createMiddlewareSupabaseClient } from '@supabase/auth-helpers-nextjs'
+```tsx title="app/api/todos/route.tsx"
+import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
-import type { NextRequest } from 'next/server'
+import { cookies } from 'next/headers'
-export async function middleware(req: NextRequest) {
- // We need to create a response and hand it to the supabase client to be able to modify the response headers.
- const res = NextResponse.next()
- // Create authenticated Supabase Client.
- const supabase = createMiddlewareSupabaseClient({ req, res })
- // Check if we have a session
- const {
- data: { session },
- } = await supabase.auth.getSession()
+import type { Database } from '@/lib/database.types'
- // Check auth condition
- if (session?.user.email?.endsWith('@gmail.com')) {
- // Authentication successful, forward request to protected route.
- return res
- }
-
- // Auth condition not met, redirect to home page.
- const redirectUrl = req.nextUrl.clone()
- redirectUrl.pathname = '/'
- redirectUrl.searchParams.set(`redirectedFrom`, req.nextUrl.pathname)
- return NextResponse.redirect(redirectUrl)
-}
-
-export const config = {
- matcher: '/middleware-protected',
+export async function POST(request: Request) {
+ const { title } = await request.json()
+ const supabase = createRouteHandlerClient({ cookies })
+ const { data } = await supabase.from('todos').insert({ title }).select()
+ return NextResponse.json(data)
}
```
+> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createRouteHandlerClient` to add type support to the Supabase client.
+
-### Migrating to v0.4.X and supabase-js v2
+### Middleware
-With the update to `supabase-js` v2 the `auth` API routes are no longer required, therefore you can go ahead and delete your `auth` directory under the `/pages/api/` directory. Please refer to the [v2 migration guide](/docs/reference/javascript/v1/upgrade-guide) for the full set of changes within supabase-js.
+See [refreshing session example](/docs/guides/auth/auth-helpers/nextjs#refresh-session-with-middleware) above.
-The `/api/auth/logout` API route has been removed, please use the `signout` method instead:
+## More examples
-```jsx
-
-```
-
-The `supabaseClient` and `supabaseServerClient` have been removed in favor of the `createBrowserSupabaseClient` and `createServerSupabaseClient` methods. This allows you to provide the CLI-generated types to the client:
-
-```tsx
-// client-side
-import type { Database } from 'types_db'
-const [supabaseClient] = useState(() => createBrowserSupabaseClient())
-
-// server-side API route
-import type { NextApiRequest, NextApiResponse } from 'next'
-import type { Database } from 'types_db'
-
-export default async (req: NextApiRequest, res: NextApiResponse) => {
- const supabaseServerClient = createServerSupabaseClient({
- req,
- res,
- })
- const {
- data: { user },
- } = await supabaseServerClient.auth.getUser()
-
- res.status(200).json({ name: user?.name ?? '' })
-}
-```
-
-- The `UserProvider` has been replaced by the `SessionContextProvider`. Make sure to wrap your `pages/_app.js` componenent with the `SessionContextProvider`. Then, throughout your application you can use the `useSessionContext` hook to get the `session` and the `useSupabaseClient` hook to get an authenticated `supabaseClient`.
-- The `useUser` hook now returns the `user` object or `null`.
-- Usage with TypeScript: You can pass types that were [generated with the Supabase CLI](/docs/reference/javascript/typescript-support#generating-types) to the Supabase Client to get enhanced type safety and auto completion:
-
-Creating a new supabase client object:
-
-```tsx
-import { Database } from '../database.types'
-
-const [supabaseClient] = useState(() => createBrowserSupabaseClient())
-```
-
-Retrieving a supabase client object from the SessionContext:
-
-```tsx
-import { useSupabaseClient } from '@supabase/auth-helpers-react'
-import { Database } from '../database.types'
-
-const supabaseClient = useSupabaseClient()
-```
+- [Full App Router repo](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs)
+- [Realtime](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/realtime-posts.tsx)
+- [Protected Routes](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/[id]/page.tsx)
+- [Conditional Rendering in Client Components with SSR](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs/app/login-form.tsx)
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/auth/auth-helpers/remix.mdx b/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
index 7ff90a03904..6fb00ba51c6 100644
--- a/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
+++ b/apps/docs/pages/guides/auth/auth-helpers/remix.mdx
@@ -441,7 +441,7 @@ useEffect(() => {
return () => {
subscription.unsubscribe()
}
-}, [serverAccessToken, supabase, fetcher])
+}, [serverAccessToken, supabase, revalidate])
```
@@ -471,7 +471,7 @@ useEffect(() => {
return () => {
subscription.unsubscribe()
}
-}, [serverAccessToken, supabase, fetcher])
+}, [serverAccessToken, supabase, revalidate])
```
diff --git a/apps/docs/pages/guides/functions.mdx b/apps/docs/pages/guides/functions.mdx
index 43766eb0b21..bb8157e871b 100644
--- a/apps/docs/pages/guides/functions.mdx
+++ b/apps/docs/pages/guides/functions.mdx
@@ -113,6 +113,11 @@ export const examples = [
description: `Request Routing with Oak server middleware.`,
href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/oak-server',
},
+ {
+ name: 'Hugging Face',
+ description: `Access 100,000+ Machine Learning models.`,
+ href: '/guides/functions/examples/huggingface-image-captioning',
+ },
{
name: 'OpenAI',
description: `Using OpenAI in Edge Functions.`,
diff --git a/apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx b/apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx
new file mode 100644
index 00000000000..087567f9650
--- /dev/null
+++ b/apps/docs/pages/guides/functions/examples/huggingface-image-captioning.mdx
@@ -0,0 +1,104 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ title: 'Hugging Face',
+ description:
+ 'Use the Hugging Face Inference API to make calls to 100,000+ Machine Learning models from Supabase Edge Functions.',
+ video: 'https://www.youtube.com/v/OgnYxRkxEUw',
+ tocVideo: 'OgnYxRkxEUw',
+}
+
+[Hugging Face](https://huggingface.co/) is the collaboration platform for the machine learning community.
+
+[Huggingface.js](https://huggingface.co/docs/huggingface.js/index) provides a convenient way to make calls to 100,000+ Machine Learning models, making it easy to incorporate AI functionality into your [Supabase Edge Functions](https://supabase.com/edge-functions).
+
+Putting this together with [Supabase Storage](https://supabase.com/storage) and [Database Webhooks](https://supabase.com/docs/guides/database/webhooks) we can easily put together a service that automatically generates captions for any image we upload to a storage bucket.
+
+
+
+
+
+## Setup
+
+- Open your Supabase project dashboard or [create a new project](https://app.supabase.com/projects).
+- [Create a new bucket](https://app.supabase.com/project/_/storage/buckets) called `images`.
+- Generate TypeScript types from remote Database.
+- Create a new Database table called `image_caption`.
+ - Create `id` column of type `uuid` which references `storage.objects.id`.
+ - Create a `caption` column of type `text`.
+- Regenerate TypeScript types to include new `image_caption` table.
+- Deploy the function to Supabase: `supabase functions deploy huggingface-image-captioning`.
+- Create the Database Webhook in the [Supabase Dashboard](https://app.supabase.com/project/_/database/hooks) to trigger the `huggingface-image-captioning` function anytime a record is added to the `storage.objects` table.
+
+## Generate TypeScript Types
+
+To generate the types.ts file for the storage and public schemas, run the following command in the terminal:
+
+```bash
+supabase gen types typescript --project-id=your-project-ref --schema=storage,public > supabase/functions/huggingface-image-captioning/types.ts
+```
+
+## Code
+
+Find the complete code on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/huggingface-image-captioning).
+
+```ts
+import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
+import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
+import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.7.1'
+import { Database } from './types.ts'
+
+console.log('Hello from `huggingface-image-captioning` function!')
+
+const hf = new HfInference(Deno.env.get('HUGGINGFACE_ACCESS_TOKEN'))
+
+type SoRecord = Database['storage']['Tables']['objects']['Row']
+interface WebhookPayload {
+ type: 'INSERT' | 'UPDATE' | 'DELETE'
+ table: string
+ record: SoRecord
+ schema: 'public'
+ old_record: null | SoRecord
+}
+
+serve(async (req) => {
+ const payload: WebhookPayload = await req.json()
+ const soRecord = payload.record
+ const supabaseAdminClient = createClient(
+ // Supabase API URL - env var exported by default when deployed.
+ Deno.env.get('SUPABASE_URL') ?? '',
+ // Supabase API SERVICE ROLE KEY - env var exported by default when deployed.
+ Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
+ )
+
+ // Construct image url from storage
+ const { data, error } = await supabaseAdminClient.storage
+ .from(soRecord.bucket_id!)
+ .createSignedUrl(soRecord.path_tokens!.join('/'), 60)
+ if (error) throw error
+ const { signedUrl } = data
+
+ // Run image captioning with Huggingface
+ const imgDesc = await hf.imageToText({
+ data: await (await fetch(signedUrl)).blob(),
+ model: 'nlpconnect/vit-gpt2-image-captioning',
+ })
+
+ // Store image caption in Database table
+ await supabaseAdminClient
+ .from('image_caption')
+ .insert({ id: soRecord.id!, caption: imgDesc.generated_text })
+ .throwOnError()
+
+ return new Response('ok')
+})
+```
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/forestadmin.mdx b/apps/docs/pages/guides/integrations/forestadmin.mdx
new file mode 100644
index 00000000000..910a42f3473
--- /dev/null
+++ b/apps/docs/pages/guides/integrations/forestadmin.mdx
@@ -0,0 +1,53 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'forestadmin',
+ title: 'ForestAdmin',
+ description:
+ 'Get started with Supabase and Forest Admin, a tool for automatically generating an Admin Panel without having to build it.',
+}
+
+This guide outlines how to instantly generate an Admin Panel on top of your Supabase backend.
+
+[Forest Admin](https://www.forestadmin.com/) offers an off-the-shelf Admin Panel system that can reduce the amount of time and effort needed to create, maintain, and manage internal tools.
+It automatically builds a backend API and provides a user-friendly interface to Create, Read, Update and Delete, Search, Segment your data, trigger custom actions, control permissions, and set up workflows on top of your app's data.
+
+
+
+If you don’t have a Forest Admin account, you can create one in a few minutes [here](https://app.forestadmin.com/signup).
+
+Let’s get started!
+
+## Step 1: Configure your Supabase Backend
+If you already have a Supabase project set up, simply go to the Project Settings / Database tab to access the Database Settings and retrieve the connection string (URI tab). This is the only information you will need to connect your Supabase account to Forest Admin.
+
+
+
+If you don't have anything set up on Supabase yet, you can [create a Project](https://app.supabase.com/new/_) in just a few seconds. Once done, just go to the Database tab and create your first table.
+
+
+
+## Step 2: Connect the database to Forest Admin
+
+First, you have to create a new project on Forest Admin:
+
+
+Then, you can use Forest Admin's Instant Setup for the Cloud mode to quickly get started. Alternatively, you can host the generated backend admin API on your own (Advanced setup), giving you full control of the backend code. In this guide, we will use the Cloud mode and the Instant Setup.
+
+
+Finally, you can enter the database credentials you obtained in Step 1 and set them in Forest Admin by using the Connection URI mode. Don't forget to replace the password in the connection string with the database password you set in Supabase. Note that if you forget it, you can always go to your Supabase Database settings and reset your database password.
+
+
+## You're all done!
+
+There it is, the configuration of Forest Admin is now complete and your admin panel is now ready-to-use with all the features of an admin panel provided out of the box. You can now browse or manipulate all your data in a structured way, use search with support for complex filters, build dashboards, invite your team mates and start collaborating around your business operations and much more.
+
+## Resources
+
+- [Forest Admin](https://www.forestadmin.com/) official website.
+- [Forest Admin GitHub](https://github.com/ForestAdmin).
+- [Forest Admin](https://docs.forestadmin.com/documentation-portal/) documentation.
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/integrations/snaplet.mdx b/apps/docs/pages/guides/integrations/snaplet.mdx
index 151b6627035..74297c2670b 100644
--- a/apps/docs/pages/guides/integrations/snaplet.mdx
+++ b/apps/docs/pages/guides/integrations/snaplet.mdx
@@ -34,9 +34,6 @@ Follow along in the video below as the founder of Snaplet, Peter Pistorius, take
> To create a read-only role across all schemas you can checkout the Snaplet [docs](https://docs.snaplet.dev/guides/postgresql/#create-a-read-only-role)
-4. **Superuser access** for the development project. This can be done in Supabase via `Organization > SQL Editor > + New Query` and running this statement:
- `ALTER USER postgres WITH superuser;`
-
## Step 2: Copying your production database
### 2.1. Connect your data source
@@ -84,23 +81,14 @@ Your data target is where you want Snaplet to restore the captured snapshot of y
> Remember the password you use when creating the project. You’ll need this password to connect your database to Snaplet later.
-### 3.2. Make your `postgres` user a superuser
-
-Snaplet requires the ability to drop the database schemas whilst restoring a snapshot. In order to do that, Snaplet requires `superuser` privileges.
-
-1. Navigate to the `SQL Editor` in your Supabase console
-2. Click on `new query`
-3. Paste `alter user postgres with superuser;` into the SQL editor
-4. Run the query
-
-### 3.3. Install the Snaplet CLI
+### 3.2. Install the Snaplet CLI
1. Open your terminal and run `curl -sL https://app.snaplet.dev/get-cli/ | bash`
2. Run `snaplet auth`
3. Navigate to [https://app.snaplet.dev/access-token/cli](https://app.snaplet.dev/access-token/cli) to get your access token
4. Paste your access token in the terminal
-### 3.4. Restore to the data target
+### 3.3. Restore to the data target
You're now ready to restore your production snapshot into your Supabase development project.
diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt
index db94336b341..7bf03ef1536 100644
--- a/apps/docs/public/humans.txt
+++ b/apps/docs/public/humans.txt
@@ -16,6 +16,7 @@ Chase Granberry
Chris Copplestone
Dave Wilson
Div Arora
+Divit D
Egor Romanov
Fabrizio Fenoglio
Francesco Sansalvadore
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png b/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png
new file mode 100644
index 00000000000..62bf401b5d0
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forest-admin-collections.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png
new file mode 100644
index 00000000000..5e21934d1db
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-create-project.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png
new file mode 100644
index 00000000000..48492f5b041
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-db-credentials.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png
new file mode 100644
index 00000000000..7087ea22f74
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/forestadmin-hosting.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png
new file mode 100644
index 00000000000..20f2a470c3b
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-create-table.png differ
diff --git a/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png
new file mode 100644
index 00000000000..cf13d0fa951
Binary files /dev/null and b/apps/docs/public/img/guides/integrations/forestadmin/supabase-db-credentials.png differ
diff --git a/apps/docs/public/sitemap.xml b/apps/docs/public/sitemap.xml
index c3d36c42b96..e3d783ddd46 100644
--- a/apps/docs/public/sitemap.xml
+++ b/apps/docs/public/sitemap.xml
@@ -835,13 +835,13 @@
- https://supabase.com/docs/guides/auth/auth-helpers/nextjs-server-components
+ https://supabase.com/docs/guides/auth/auth-helpers/nextjsweekly0.5
- https://supabase.com/docs/guides/auth/auth-helpers/nextjs
+ https://supabase.com/docs/guides/auth/auth-helpers/nextjs-pagesweekly0.5
diff --git a/apps/docs/styles/main.scss b/apps/docs/styles/main.scss
index 08c65126db1..ce93b3296a2 100644
--- a/apps/docs/styles/main.scss
+++ b/apps/docs/styles/main.scss
@@ -25,7 +25,7 @@
}
body {
- @apply bg-scale-200 dark:bg-[#1c1c1c] #{!important};
+ @apply bg-scale-200;
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
diff --git a/apps/docs/tailwind.config.js b/apps/docs/tailwind.config.js
index 9c2af498997..4da8605b88e 100644
--- a/apps/docs/tailwind.config.js
+++ b/apps/docs/tailwind.config.js
@@ -1 +1,24 @@
-module.exports = require('config/tailwind.config')
+const config = require('config/tailwind.config')
+
+module.exports = config({
+ content: [
+ './../../packages/common/**/*.{ts,tsx}',
+ './../../packages/ui/**/*.{tsx,ts,js}',
+ './pages/**/*.{tsx,mdx}',
+ './components/**/*.tsx',
+ './layouts/**/*.tsx',
+ './src/**/*.{ts,tsx,mdx}',
+ './docs/**/*.{tsx,mdx}',
+ ],
+ plugins: [
+ function ({ addUtilities, addVariant }) {
+ addUtilities({
+ // prose (tailwind typography) helpers
+ // useful for removing margins in prose styled sections
+ '.prose--remove-p-margin p': {
+ margin: '0',
+ },
+ })
+ },
+ ],
+})
diff --git a/apps/www/_blog/2021-07-26-supabase-community-day.mdx b/apps/www/_blog/2021-07-26-supabase-community-day.mdx
index c81987dc03d..488946a5afe 100644
--- a/apps/www/_blog/2021-07-26-supabase-community-day.mdx
+++ b/apps/www/_blog/2021-07-26-supabase-community-day.mdx
@@ -105,14 +105,14 @@ Today the Community are releasing both the Flutter and Dart libraries in Beta (w
- Check out the [Flutter Quickstart Guide](/docs/guides/with-flutter)
- Check out the code:
- - Supabase Flutter: [GitHub](https://github.com/supabase/supabase-flutter/) | [Release](https://pub.dev/packages/supabase_flutter)
- - Supabase Dart: [GitHub](https://github.com/supabase/supabase-dart/) | [Release](https://pub.dev/packages/supabase)
+ - Supabase Flutter: [GitHub](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter) | [Release](https://pub.dev/packages/supabase_flutter)
+ - Supabase Dart: [GitHub](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase) | [Release](https://pub.dev/packages/supabase)
### Get involved
- Help [write](/supasquad#author) the Flutter Docs.
-- Help [maintain](/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/)
- and [Dart](https://github.com/supabase/supabase-dart/) libraries.
+- Help [maintain](/supasquad#maintainer) the [Flutter](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase_flutter)
+ and [Dart](https://github.com/supabase/supabase-flutter/tree/main/packages/supabase) libraries.
## Supabase Discord
diff --git a/apps/www/_blog/2023-04-12-storage-v3-resumable-uploads.mdx b/apps/www/_blog/2023-04-12-storage-v3-resumable-uploads.mdx
index 57db2288f05..3a46a11b225 100644
--- a/apps/www/_blog/2023-04-12-storage-v3-resumable-uploads.mdx
+++ b/apps/www/_blog/2023-04-12-storage-v3-resumable-uploads.mdx
@@ -25,7 +25,7 @@ Resumable uploads divides the file into chunks before uploading them, emitting p
/>
-With this release, you can now upload files as large as 50GB! (Previously the limit was 5GB).
+With this release, users on the Pro tier or higher can now upload files as large as 50GB! This substantial upgrade from the previous limit of 5GB offers even more flexibility for your file uploads.
To build this feature, we implemented Postgres Advisory locks which solved some gnarly concurrency problems. We can now handle edge-cases, like two clients uploading to the same location. We’ll deep dive into how we implemented Advisory locks later in the post.
diff --git a/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx b/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx
new file mode 100644
index 00000000000..c094a0f0826
--- /dev/null
+++ b/apps/www/_blog/2023-05-25-chatgpt-plugins-support-postgres.mdx
@@ -0,0 +1,268 @@
+---
+title: 'ChatGPT plugins now support Postgres & Supabase'
+description: Supabase recently contributed to the OpenAI Retrieval Plugin repo with a Postgres and a Supabase implementation to help developers build ChatGPT plugins using pgvector.
+tags:
+ - AI
+ - tutorial
+ - postgres
+date: '2023-05-25'
+toc_depth: 2
+author: egor_romanov,paul_copplestone
+image: 2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugins-support-postgres.jpeg
+thumb: 2023-05-25-chatgpt-plugins-support-postgres/chatgpt-plugins-support-postgres.jpeg
+---
+
+One of the challenges that ChatGPT faces is being able to answer questions from a private dataset. We can solve this with “retrieval plugins”, which allow ChatGPT to access information from a database.
+
+Supabase recently contributed to the OpenAI repo with a [Postgres](https://github.com/openai/chatgpt-retrieval-plugin#postgres) and a [Supabase](https://github.com/openai/chatgpt-retrieval-plugin#supabase) implementation to help developers build plugins using pgvector.
+
+Let’s dig into the specifics of Retrieval plugins, then we can implement an example - we’ll ingest all of the Postgres docs into a Supabase database, then get ChatGPT to answer questions. It’s a contrived example since ChatGPT already knows about Postgres, but what other data source would Supabase want to use?
+
+## What is ChatGPT Retrieval Plugin?
+
+ChatGPT recently released [Plugins](https://openai.com/blog/chatgpt-plugins) which help ChatGPT access up-to-date information, run computations, or use third-party services.
+
+A [Retrieval Plugin](https://github.com/openai/chatgpt-retrieval-plugin) is a Python project designed to inject external data into the ChatGPT. It allows ChatGPT to dynamically pull relevant information into conversations from your data sources. This could be PDF documents, Confluence, or Notion knowledge bases.
+
+A retrieval plugin does a few things:
+
+1. Turn documents into smaller chunks.
+2. Converts chunks into embeddings using OpenAI's `text-embedding-ada-002` model.
+3. Stores the embeddings into a vector database.
+4. Queries the vector database for relevant documents when a question is asked.
+
+You can choose your preferred vector database provider from a list of [supported options](https://github.com/openai/chatgpt-retrieval-plugin#choosing-a-vector-database).
+
+## Adding Supabase and Postgres as Datastore options for ChatGPT Retrieval Plugin
+
+We've implemented two vector provider options: one for Postgres and one for Supabase. The main differences are:
+
+- The Postgres version uses the `psycopg2` python library to directly connect to the database.
+- The Supabase version interacts with the database via PostgREST. This is helpful if you want to use [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security) or if you are planning to use the data in the Retrieval store beyond ChatGPT.
+
+The Postgres implementation is great to start with because there are now a [large number of providers](https://github.com/pgvector/pgvector/issues/54) supporting pgvector.
+
+Both have the same schema so you can easily switch between them:
+
+```sql
+create table if not exists documents (
+ id text primary key default gen_random_uuid()::text,
+ source text,
+ source_id text,
+ content text,
+ document_id text,
+ author text,
+ url text,
+ created_at timestamptz default now(),
+ embedding vector(1536)
+);
+```
+
+When you create the retrieval store inside your database, a stored function is implemented to query and find relevant information for your question to ChatGPT:
+
+```sql
+create or replace function match_page_sections(
+ in_embedding vector(1536),
+ in_match_count int default 3,
+ in_document_id text default '%%',
+ in_source_id text default '%%',
+ in_source text default '%%',
+ in_author text default '%%',
+ in_start_date timestamptz default '-infinity',
+ in_end_date timestamptz default 'infinity'
+)
+returns table (
+ id text,
+ source text,
+ source_id text,
+ document_id text,
+ url text,
+ created_at timestamptz,
+ author text,
+ content text,
+ embedding vector(1536),
+ similarity float
+)
+language plpgsql
+as $$
+#variable_conflict use_variable
+begin
+return query
+
+select
+ documents.id,
+ documents.source,
+ documents.source_id,
+ documents.document_id,
+ documents.url,
+ documents.created_at,
+ documents.author,
+ documents.content,
+ documents.embedding,
+ (documents.embedding <#> in_embedding) * -1 as similarity
+from
+ documents
+where
+ in_start_date <= documents.created_at and
+ documents.created_at <= in_end_date and
+ (documents.source_id like in_source_id or documents.source_id is null) and
+ (documents.source like in_source or documents.source is null) and
+ (documents.author like in_author or documents.author is null) and
+ (documents.document_id like in_document_id or documents.document_id is null)
+order by
+ documents.embedding <#> in_embedding
+limit
+ in_match_count;
+end;
+$$;
+```
+
+We apply filters based on the source, author, document, and date, and find the closest embeddings using the inner product distance function. This function offers the best performance when the embeddings are normalized, which is the case for OpenAI embeddings. The similarity is calculated as: `(documents.embedding <#> in_embedding) * -1 as similarity`. And that’s it, you can now seamlessly use the Retrieval Plugin with a Postgres Database underneath, eliminating the need for any manual implementation on your end.
+
+## Example: Chat with Postgres Docs
+
+Let’s build an example where we can “ask ChatGPT questions” about the Postgres documentation.
+
+This will require several steps:
+
+1. Download all the [Postgres docs as a PDF](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf)
+2. Convert the docs into chunks of embedded text and store them in Supabase
+3. Run our plugin locally so that we can ask questions about the Postgres docs.
+
+
+
+
+
+### Step 1: Fork the ChatGPT Retrieval Plugin repository
+
+Fork the ChatGPT Retrieval Plugin repository to your GitHub account and clone it to your local machine. Read through the `README.md` file to understand the project structure.
+
+### Step 2: Install dependencies
+
+Choose your desired datastore provider and remove unused dependencies from `pyproject.toml`. For this example, we'll use Supabase. And install dependencies with Poetry:
+
+```bash
+poetry install
+```
+
+### Step 3: Create a Supabase project
+
+Create a [Supabase project](https://supabase.com/dashboard) and database by following the instructions [here](https://supabase.com/docs/guides/platform). Export the environment variables required for the retrieval plugin to work:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=supabase
+export SUPABASE_URL=
+export SUPABASE_SERVICE_ROLE_KEY=
+```
+
+For Postgres datastore, you'll need to export these environment variables instead:
+
+```bash
+export OPENAI_API_KEY=
+export DATASTORE=postgres
+export PG_HOST=
+export PG_PASSWORD=
+```
+
+### Step 4: Run Postgres Locally
+
+To start quicker you may use Supabase CLI to spin everything up locally as it already includes pgvector from the start. Install `supabase-cli`, go to the `examples/providers` folder in the repo and run:
+
+```bash
+supabase start
+```
+
+This will pull all docker images and run supabase stack in docker on your local machine. It will also apply all the necessary migrations to set the whole thing up. You can then use your local setup the same way, just export the environment variables and follow to the next steps.
+
+Using `supabase-cli` is not required and you can use any other docker image or hosted version of PostgresDB that includes `pgvector`. Just make sure you run migrations from `examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql`.
+
+### Step 5: Obtain OpenAI API key
+
+To create embeddings Plugin uses OpenAI API and `text-embedding-ada-002` model. Each time we add some data to our datastore, or try to query relevant information from it, embedding will be created either for inserted data chunk, or for the query itself. To make it work we need to export `OPENAI_API_KEY`. If you already have an account in OpenAI, you just need to go to [User Settings - API keys](https://platform.openai.com/account/api-keys) and Create new secret key.
+
+
+
+### Step 6: Run the plugin!
+
+Execute the following command to run the plugin:
+
+```bash
+poetry run dev
+# output
+INFO: Will watch for changes in these directories: ['./chatgpt-retrieval-plugin']
+INFO: Uvicorn running on http://localhost:3333 (Press CTRL+C to quit)
+INFO: Started reloader process [87843] using WatchFiles
+INFO: Started server process [87849]
+INFO: Waiting for application startup.
+INFO: Application startup complete.
+```
+
+The plugin will start on your localhost - port :3333 by default.
+
+### Step 6: Populating data in the datastore
+
+For this example, we'll upload Postgres documentation to the datastore. Download the [Postgres documentation](https://www.postgresql.org/files/documentation/pdf/15/postgresql-15-US.pdf) and use the `/upsert-file` endpoint to upload it:
+
+```bash
+curl -X POST -F \\"file=@./postgresql-15-US.pdf\\"
+```
+
+The plugin will split your data and documents into smaller chunks automatically. You can view the chunks using the Supabase dashboard or any other SQL client you prefer. For the whole Postgres Documentation I got 7,904 records in my documents table, which is not a lot, but we can try to add index for `embedding` column to speed things up by a little. To do so, you should run the following SQL command:
+
+```sql
+create index on documents
+using ivfflat (embedding vector_ip_ops)
+with (lists = 10);
+```
+
+This will create an index for the inner product distance function. Important to note that it is an approximate index. It will change the logic from performing the exact nearest neighbor search to the approximate nearest neighbor search.
+
+We are using `lists = 10`, because as a general guideline, you should start looking for optimal lists constant value with the formula: `rows / 1000` when you have less than 1 million records in your table.
+
+Now, it is time to add our plugin to ChatGPT.
+
+### Empowering ChatGPT with Postgres knowledge
+
+To integrate our plugin with ChatGPT, register it in the ChatGPT dashboard. Assuming you have access to ChatGPT Plugins and plugin development, select the Plugins model in a new chat, then choose "Plugin store" and "Develop your own plugin." Enter `localhost:3333` into the domain input, and your plugin is now part of ChatGPT.
+
+
+
+
+
+
+
+You can now ask questions about Postgres and receive answers derived from the documentation!
+
+Let's try it out: ask ChatGPT to find out when to use `check` and when to use `using`. You will be able to see what queries were sent to our plugin and what it responded to.
+
+
+
+
+
+And after ChatGPT receives a response from the plugin it will answer your question with the data from the documentation.
+
+
+
+## Wrap up
+
+It's easy to bring any context into the datastore and utilize it with ChatGPT. Simply export your knowledge base from platforms like Notion or Confluence, upload it to the datastore, and you're good to go. You can also use any other datastore provider you prefer.
+
+And the good news is that you’re not limited by using it with ChatGPT, you can embed it in your website or documentation, and build a Slack bot or telegram bot to answer questions about your company or product. For that, you will only need to add a single call to OpenAI API to create a summary of data retrieved from the Plugin. You can find some inspiration on how to do that in our blog post about building [Supabase Clippy assistant](https://supabase.com/blog/chatgpt-supabase-docs).
+
+Let us know on [Twitter](https://twitter.com/Supabase) if you are building ChatGPT Plugins. We can’t wait to see what you will build!
+
+## More AI resources
+
+- [OpenAI ChatGPT Plugin docs](https://platform.openai.com/docs/plugins/introduction)
+- [ChatGPT Retrieval Plugin Repo](https://github.com/openai/chatgpt-retrieval-plugin)
+- [How to build ChatGPT Plugin from scratch with Supabase Edge Runtime](https://supabase.com/blog/building-chatgpt-plugins-template).
+- [Docs pgvector: Embeddings and vector similarity](https://supabase.com/docs/guides/database/extensions/pgvector)
diff --git a/apps/www/_customers/markprompt.mdx b/apps/www/_customers/markprompt.mdx
index 200ae34564d..49808e441ca 100644
--- a/apps/www/_customers/markprompt.mdx
+++ b/apps/www/_customers/markprompt.mdx
@@ -55,7 +55,7 @@ Markprompt were also impressed with the ease of use and scalability of Supabase.
## What They Built
-Markprompt is a set of API endpoints that allow you to train your content and create a prompt to ask questions to it, for instance for a docs site. Markprompt also designed a user-friendly web dashboard that simplifies content training, synchronization with GitHub repos or websites, access key management, and provides insights into user query statistics. Furthermore, Markprompt's UI components, available as React and Web Components, allow integration of prompts at all touch points of a company, from public websites websites to internal knowledge bases with just a few lines of code.
+Markprompt is a set of API endpoints that allow you to train your content and create a prompt to ask questions to it, for instance for a docs site. Markprompt also designed a user-friendly web dashboard that simplifies content training, synchronization with GitHub repos or websites, access key management, and provides insights into user query statistics. Furthermore, Markprompt's UI components, available as React and Web Components, allow integration of prompts at all touch points of a company, from public websites to internal knowledge bases with just a few lines of code.
Like Supabase, Markprompt is an open-source platform, allowing customers the freedom to host the dashboard and model backend on their own premises. Markprompt warmly welcomes contributions from the community, fostering collaboration and continuous improvement of the platform to meet the evolving needs of developers and technical users.
diff --git a/apps/www/components/CTABanner/index.tsx b/apps/www/components/CTABanner/index.tsx
index d1313ad6e12..fb08eb5630e 100644
--- a/apps/www/components/CTABanner/index.tsx
+++ b/apps/www/components/CTABanner/index.tsx
@@ -16,7 +16,7 @@ const CTABanner = (props: any) => {