From ef78695aa749e0fe8c1f7ce00be9f5747b923e86 Mon Sep 17 00:00:00 2001 From: David Plugge Date: Wed, 10 May 2023 21:26:35 +0200 Subject: [PATCH 01/15] change prettier config --- .prettierrc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.prettierrc b/.prettierrc index 32ebab4e..a558fd4a 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,28 @@ { + "useTabs": true, "singleQuote": true, - "trailingComma": "none" + "tabWidth": 4, + "trailingComma": "none", + "printWidth": 100, + "overrides": [ + { + "files": ["*.svelte"], + "options": { + "bracketSameLine": false + } + }, + { + "files": ["**/README.md"], + "options": { + "useTabs": false, + "tabWidth": 2 + } + }, + { + "files": ["**/CHANGELOG.md"], + "options": { + "requirePragma": true + } + } + ] } From 358efd0a9a53979e5478ae316f48cb2a5dc66cf5 Mon Sep 17 00:00:00 2001 From: David Plugge Date: Wed, 10 May 2023 23:04:16 +0200 Subject: [PATCH 02/15] format md files with spaces --- .prettierrc | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.prettierrc b/.prettierrc index a558fd4a..06040682 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,28 +1,28 @@ { - "useTabs": true, - "singleQuote": true, - "tabWidth": 4, - "trailingComma": "none", - "printWidth": 100, - "overrides": [ - { - "files": ["*.svelte"], - "options": { - "bracketSameLine": false - } - }, - { - "files": ["**/README.md"], - "options": { - "useTabs": false, - "tabWidth": 2 - } - }, - { - "files": ["**/CHANGELOG.md"], - "options": { - "requirePragma": true - } - } - ] + "useTabs": true, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "none", + "printWidth": 100, + "overrides": [ + { + "files": ["*.svelte"], + "options": { + "bracketSameLine": false + } + }, + { + "files": ["*.md"], + "options": { + "useTabs": false, + "tabWidth": 2 + } + }, + { + "files": ["**/CHANGELOG.md"], + "options": { + "requirePragma": true + } + } + ] } From a325e8a84a62256bfb61317d35a25c7725e57511 Mon Sep 17 00:00:00 2001 From: David Plugge Date: Wed, 10 May 2023 23:04:23 +0200 Subject: [PATCH 03/15] format files --- .../nextjs-server-components/app/head.tsx | 16 +- .../nextjs-server-components/app/layout.tsx | 40 ++- .../app/optional-session/page.tsx | 6 +- .../nextjs-server-components/app/page.tsx | 79 +++-- .../app/realtime/new-post.tsx | 40 +-- .../app/realtime/page.tsx | 22 +- .../app/realtime/realtime-posts.tsx | 60 ++-- .../app/required-session/page.tsx | 6 +- .../components/login.tsx | 84 ++--- .../components/supabase-listener.tsx | 42 ++- .../components/supabase-provider.tsx | 24 +- examples/nextjs-server-components/db_types.ts | 74 ++--- .../nextjs-server-components/middleware.tsx | 26 +- .../pages/api/hello.ts | 9 +- .../utils/supabase-browser.ts | 3 +- .../utils/supabase-server.ts | 8 +- examples/nextjs/db_types.ts | 120 ++++--- examples/nextjs/middleware.ts | 38 +-- examples/nextjs/pages/_app.tsx | 48 ++- examples/nextjs/pages/api/callback.ts | 14 +- examples/nextjs/pages/api/protected-route.ts | 29 +- .../nextjs/pages/github-provider-token.tsx | 88 +++--- examples/nextjs/pages/index.tsx | 130 ++++---- examples/nextjs/pages/profile.tsx | 62 ++-- examples/nextjs/pages/protected-page.tsx | 82 +++-- examples/remix/app/entry.client.tsx | 24 +- examples/remix/app/entry.server.tsx | 172 +++++----- examples/remix/app/root.tsx | 43 ++- examples/remix/app/routes/__supabase.tsx | 139 ++++----- .../remix/app/routes/__supabase/callback.tsx | 22 +- .../remix/app/routes/__supabase/index.tsx | 2 +- .../routes/__supabase/optional-session.tsx | 36 +-- .../remix/app/routes/__supabase/realtime.tsx | 48 ++- .../routes/__supabase/required-session.tsx | 62 ++-- examples/remix/components/login.tsx | 76 ++--- examples/remix/components/nav.tsx | 26 +- examples/remix/components/realtime-posts.tsx | 44 ++- examples/remix/db_types.ts | 74 ++--- examples/remix/utils/supabase.server.ts | 17 +- packages/nextjs/MIGRATION_GUIDE.md | 16 +- packages/nextjs/src/browserClient.ts | 70 ++--- packages/nextjs/src/middlewareClient.ts | 154 ++++----- packages/nextjs/src/serverClient.ts | 166 +++++----- packages/nextjs/src/serverComponentClient.ts | 131 ++++---- packages/nextjs/tsup.config.ts | 30 +- .../react/src/components/SessionContext.tsx | 294 +++++++++--------- packages/react/tsup.config.ts | 22 +- packages/remix/src/createSupabaseClient.ts | 220 +++++++------ packages/remix/src/index.ts | 5 +- packages/remix/tsup.config.ts | 30 +- packages/shared/src/browserCookieStorage.ts | 46 +-- .../shared/src/cookieAuthStorageAdapter.ts | 73 ++--- packages/shared/src/createClient.ts | 58 ++-- packages/shared/src/index.ts | 12 +- packages/shared/src/types.ts | 8 +- packages/shared/src/utils/constants.ts | 4 +- packages/shared/src/utils/cookies.ts | 115 ++++--- packages/shared/src/utils/helpers.ts | 2 +- packages/shared/tsup.config.ts | 22 +- packages/sveltekit/README.md | 23 +- packages/sveltekit/src/loadStorageAdapter.ts | 30 +- .../sveltekit/src/serverStorageAdapter.ts | 117 ++++--- packages/sveltekit/src/supabaseLoadClient.ts | 105 +++---- .../sveltekit/src/supabaseServerClient.ts | 76 ++--- packages/sveltekit/tsup.config.ts | 30 +- 65 files changed, 1812 insertions(+), 1982 deletions(-) diff --git a/examples/nextjs-server-components/app/head.tsx b/examples/nextjs-server-components/app/head.tsx index f11b2596..9aa05a50 100644 --- a/examples/nextjs-server-components/app/head.tsx +++ b/examples/nextjs-server-components/app/head.tsx @@ -1,10 +1,10 @@ export default function Head() { - return ( - <> - Create Next App - - - - - ); + return ( + <> + Create Next App + + + + + ); } diff --git a/examples/nextjs-server-components/app/layout.tsx b/examples/nextjs-server-components/app/layout.tsx index f968256f..730df2ef 100644 --- a/examples/nextjs-server-components/app/layout.tsx +++ b/examples/nextjs-server-components/app/layout.tsx @@ -14,31 +14,27 @@ export type TypedSupabaseClient = SupabaseClient; // do not cache this layout export const revalidate = 0; -export default async function RootLayout({ - children -}: { - children: React.ReactNode; -}) { - const supabase = createServerClient(); +export default async function RootLayout({ children }: { children: React.ReactNode }) { + const supabase = createServerClient(); - const { - data: { session } - } = await supabase.auth.getSession(); + const { + data: { session } + } = await supabase.auth.getSession(); - return ( - - {/* + return ( + + {/* will contain the components returned by the nearest parent head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head */} - - - - - - {children} - - - - ); + + + + + + {children} + + + + ); } diff --git a/examples/nextjs-server-components/app/optional-session/page.tsx b/examples/nextjs-server-components/app/optional-session/page.tsx index 3761dd11..1dc1844b 100644 --- a/examples/nextjs-server-components/app/optional-session/page.tsx +++ b/examples/nextjs-server-components/app/optional-session/page.tsx @@ -7,8 +7,8 @@ export const revalidate = 0; // this page will display with or without a user session export default async function OptionalSession() { - const supabase = createServerClient(); - const { data } = await supabase.from('posts').select('*'); + const supabase = createServerClient(); + const { data } = await supabase.from('posts').select('*'); - return
{JSON.stringify({ data }, null, 2)}
; + return
{JSON.stringify({ data }, null, 2)}
; } diff --git a/examples/nextjs-server-components/app/page.tsx b/examples/nextjs-server-components/app/page.tsx index ba3e43d0..484af069 100644 --- a/examples/nextjs-server-components/app/page.tsx +++ b/examples/nextjs-server-components/app/page.tsx @@ -2,49 +2,48 @@ import Image from 'next/image'; import styles from './page.module.css'; export default function Home() { - return ( -
-
-

- Welcome to Supabase on{' '} - Next.js 13! -

+ return ( + +
- -
- ); + + + ); } diff --git a/examples/nextjs-server-components/app/realtime/new-post.tsx b/examples/nextjs-server-components/app/realtime/new-post.tsx index ebe7682b..df30e4fc 100644 --- a/examples/nextjs-server-components/app/realtime/new-post.tsx +++ b/examples/nextjs-server-components/app/realtime/new-post.tsx @@ -3,27 +3,27 @@ import { useSupabase } from '../../components/supabase-provider'; export default function NewPost() { - const { supabase, session } = useSupabase(); + const { supabase, session } = useSupabase(); - const handleSubmit = async (e: React.SyntheticEvent) => { - e.preventDefault(); - const target = e.target as typeof e.target & { - post: { value: string }; - }; - const post = target.post.value; + const handleSubmit = async (e: React.SyntheticEvent) => { + e.preventDefault(); + const target = e.target as typeof e.target & { + post: { value: string }; + }; + const post = target.post.value; - await supabase.from('posts').insert({ content: post }); - // no need to refresh, as we are subscribed to db changes in `./realtime-posts.tsx` - }; + await supabase.from('posts').insert({ content: post }); + // no need to refresh, as we are subscribed to db changes in `./realtime-posts.tsx` + }; - return session ? ( - <> -
- - -
- - ) : ( -

Sign in to see posts

- ); + return session ? ( + <> +
+ + +
+ + ) : ( +

Sign in to see posts

+ ); } diff --git a/examples/nextjs-server-components/app/realtime/page.tsx b/examples/nextjs-server-components/app/realtime/page.tsx index 07317c69..27746ef9 100644 --- a/examples/nextjs-server-components/app/realtime/page.tsx +++ b/examples/nextjs-server-components/app/realtime/page.tsx @@ -10,16 +10,16 @@ export const revalidate = 0; // this component fetches the current posts server-side // and subscribes to new posts client-side export default async function Realtime() { - const supabase = createServerClient(); - const { data } = await supabase.from('posts').select('*'); + const supabase = createServerClient(); + const { data } = await supabase.from('posts').select('*'); - // data can be passed from server components to client components - // this allows us to fetch the initial posts before rendering the page - // our component will then subscribe to new posts client-side - return ( - <> - - - - ); + // data can be passed from server components to client components + // this allows us to fetch the initial posts before rendering the page + // our component will then subscribe to new posts client-side + return ( + <> + + + + ); } diff --git a/examples/nextjs-server-components/app/realtime/realtime-posts.tsx b/examples/nextjs-server-components/app/realtime/realtime-posts.tsx index 48e3a6b1..91fd0aee 100644 --- a/examples/nextjs-server-components/app/realtime/realtime-posts.tsx +++ b/examples/nextjs-server-components/app/realtime/realtime-posts.tsx @@ -10,36 +10,32 @@ type Post = Database['public']['Tables']['posts']['Row']; // realtime subscriptions need to be set up client-side // this component takes initial posts as props and automatically // updates when new posts are inserted into Supabase's `posts` table -export default function RealtimePosts({ - serverPosts -}: { - serverPosts: Post[]; -}) { - const [posts, setPosts] = useState(serverPosts); - const { supabase } = useSupabase(); - - useEffect(() => { - // this overwrites `posts` any time the `serverPosts` prop changes - // this happens when the parent Server Component is re-rendered - setPosts(serverPosts); - }, [serverPosts]); - - useEffect(() => { - // ensure you have enabled replication on the `posts` table - // https://app.supabase.com/project/_/database/replication - const channel = supabase - .channel('*') - .on( - 'postgres_changes', - { event: 'INSERT', schema: 'public', table: 'posts' }, - (payload) => setPosts([...posts, payload.new as Post]) - ) - .subscribe(); - - return () => { - supabase.removeChannel(channel); - }; - }, [supabase, setPosts, posts]); - - return
{JSON.stringify(posts, null, 2)}
; +export default function RealtimePosts({ serverPosts }: { serverPosts: Post[] }) { + const [posts, setPosts] = useState(serverPosts); + const { supabase } = useSupabase(); + + useEffect(() => { + // this overwrites `posts` any time the `serverPosts` prop changes + // this happens when the parent Server Component is re-rendered + setPosts(serverPosts); + }, [serverPosts]); + + useEffect(() => { + // ensure you have enabled replication on the `posts` table + // https://app.supabase.com/project/_/database/replication + const channel = supabase + .channel('*') + .on( + 'postgres_changes', + { event: 'INSERT', schema: 'public', table: 'posts' }, + (payload) => setPosts([...posts, payload.new as Post]) + ) + .subscribe(); + + return () => { + supabase.removeChannel(channel); + }; + }, [supabase, setPosts, posts]); + + return
{JSON.stringify(posts, null, 2)}
; } diff --git a/examples/nextjs-server-components/app/required-session/page.tsx b/examples/nextjs-server-components/app/required-session/page.tsx index c184d037..1c7fcd74 100644 --- a/examples/nextjs-server-components/app/required-session/page.tsx +++ b/examples/nextjs-server-components/app/required-session/page.tsx @@ -8,8 +8,8 @@ export const revalidate = 0; // the user will be redirected to the landing page if they are not signed in // check middleware.tsx to see how this routing rule is set export default async function RequiredSession() { - const supabase = createServerClient(); - const { data } = await supabase.from('posts').select('*'); + const supabase = createServerClient(); + const { data } = await supabase.from('posts').select('*'); - return
{JSON.stringify({ data }, null, 2)}
; + return
{JSON.stringify({ data }, null, 2)}
; } diff --git a/examples/nextjs-server-components/components/login.tsx b/examples/nextjs-server-components/components/login.tsx index a6729bfa..5dc3add2 100644 --- a/examples/nextjs-server-components/components/login.tsx +++ b/examples/nextjs-server-components/components/login.tsx @@ -4,46 +4,46 @@ import { useSupabase } from './supabase-provider'; // Supabase auth needs to be triggered client-side export default function Login() { - const { supabase, session } = useSupabase(); - - const handleEmailLogin = async () => { - const { error } = await supabase.auth.signInWithPassword({ - email: 'jon@supabase.com', - password: 'password' - }); - - if (error) { - console.log({ error }); - } - }; - - const handleGitHubLogin = async () => { - const { error } = await supabase.auth.signInWithOAuth({ - provider: 'github' - }); - - if (error) { - console.log({ error }); - } - }; - - const handleLogout = async () => { - const { error } = await supabase.auth.signOut(); - - if (error) { - console.log({ error }); - } - }; - - // this `session` is from the root loader - server-side - // therefore, it can safely be used to conditionally render - // SSR pages without issues with hydration - return session ? ( - - ) : ( - <> - - - - ); + const { supabase, session } = useSupabase(); + + const handleEmailLogin = async () => { + const { error } = await supabase.auth.signInWithPassword({ + email: 'jon@supabase.com', + password: 'password' + }); + + if (error) { + console.log({ error }); + } + }; + + const handleGitHubLogin = async () => { + const { error } = await supabase.auth.signInWithOAuth({ + provider: 'github' + }); + + if (error) { + console.log({ error }); + } + }; + + const handleLogout = async () => { + const { error } = await supabase.auth.signOut(); + + if (error) { + console.log({ error }); + } + }; + + // this `session` is from the root loader - server-side + // therefore, it can safely be used to conditionally render + // SSR pages without issues with hydration + return session ? ( + + ) : ( + <> + + + + ); } diff --git a/examples/nextjs-server-components/components/supabase-listener.tsx b/examples/nextjs-server-components/components/supabase-listener.tsx index e2e0fae5..ae7b012b 100644 --- a/examples/nextjs-server-components/components/supabase-listener.tsx +++ b/examples/nextjs-server-components/components/supabase-listener.tsx @@ -8,30 +8,26 @@ import { useSupabase } from './supabase-provider'; // this method avoids the need to pass a session down to child components // in order to re-render when the user's session changes // #elegant! -export default function SupabaseListener({ - serverAccessToken -}: { - serverAccessToken?: string; -}) { - const { supabase } = useSupabase(); - const router = useRouter(); +export default function SupabaseListener({ serverAccessToken }: { serverAccessToken?: string }) { + const { supabase } = useSupabase(); + const router = useRouter(); - useEffect(() => { - const { - data: { subscription } - } = supabase.auth.onAuthStateChange((event, session) => { - if (session?.access_token !== serverAccessToken) { - // server and client are out of sync - // reload the page to fetch fresh server data - // https://beta.nextjs.org/docs/data-fetching/mutating - router.refresh(); - } - }); + useEffect(() => { + const { + data: { subscription } + } = supabase.auth.onAuthStateChange((event, session) => { + if (session?.access_token !== serverAccessToken) { + // server and client are out of sync + // reload the page to fetch fresh server data + // https://beta.nextjs.org/docs/data-fetching/mutating + router.refresh(); + } + }); - return () => { - subscription.unsubscribe(); - }; - }, [serverAccessToken, router, supabase]); + return () => { + subscription.unsubscribe(); + }; + }, [serverAccessToken, router, supabase]); - return null; + return null; } diff --git a/examples/nextjs-server-components/components/supabase-provider.tsx b/examples/nextjs-server-components/components/supabase-provider.tsx index ba068416..9c94f2f5 100644 --- a/examples/nextjs-server-components/components/supabase-provider.tsx +++ b/examples/nextjs-server-components/components/supabase-provider.tsx @@ -8,27 +8,27 @@ import { createBrowserClient } from '../utils/supabase-browser'; type MaybeSession = Session | null; type SupabaseContext = { - supabase: TypedSupabaseClient; - session: MaybeSession; + supabase: TypedSupabaseClient; + session: MaybeSession; }; // @ts-ignore const Context = createContext(); export default function SupabaseProvider({ - children, - session + children, + session }: { - children: React.ReactNode; - session: MaybeSession; + children: React.ReactNode; + session: MaybeSession; }) { - const [supabase] = useState(() => createBrowserClient()); + const [supabase] = useState(() => createBrowserClient()); - return ( - - <>{children} - - ); + return ( + + <>{children} + + ); } export const useSupabase = () => useContext(Context); diff --git a/examples/nextjs-server-components/db_types.ts b/examples/nextjs-server-components/db_types.ts index 3986413a..64835236 100644 --- a/examples/nextjs-server-components/db_types.ts +++ b/examples/nextjs-server-components/db_types.ts @@ -1,43 +1,37 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json } - | Json[]; +export type Json = string | number | boolean | null | { [key: string]: Json } | Json[]; export interface Database { - public: { - Tables: { - posts: { - Row: { - id: string; - created_at: string; - content: string; - user_id: string; - }; - Insert: { - id?: string; - created_at?: string; - content: string; - user_id?: string; - }; - Update: { - id?: string; - created_at?: string; - content?: string; - user_id?: string; - }; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - [_ in never]: never; - }; - Enums: { - [_ in never]: never; - }; - }; + public: { + Tables: { + posts: { + Row: { + id: string; + created_at: string; + content: string; + user_id: string; + }; + Insert: { + id?: string; + created_at?: string; + content: string; + user_id?: string; + }; + Update: { + id?: string; + created_at?: string; + content?: string; + user_id?: string; + }; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + [_ in never]: never; + }; + Enums: { + [_ in never]: never; + }; + }; } diff --git a/examples/nextjs-server-components/middleware.tsx b/examples/nextjs-server-components/middleware.tsx index cbddfe51..54785a1f 100644 --- a/examples/nextjs-server-components/middleware.tsx +++ b/examples/nextjs-server-components/middleware.tsx @@ -5,21 +5,21 @@ import type { NextRequest } from 'next/server'; // this middleware refreshes the user's session and must be run // for any Server Component route that uses `createServerComponentSupabaseClient` export async function middleware(req: NextRequest) { - const res = NextResponse.next(); + const res = NextResponse.next(); - const supabase = createMiddlewareSupabaseClient({ req, res }); + const supabase = createMiddlewareSupabaseClient({ req, res }); - const { - data: { session } - } = await supabase.auth.getSession(); + const { + data: { session } + } = await supabase.auth.getSession(); - if (!session && req.nextUrl.pathname.startsWith('/required-session')) { - // 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); - } + if (!session && req.nextUrl.pathname.startsWith('/required-session')) { + // 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); + } - return res; + return res; } diff --git a/examples/nextjs-server-components/pages/api/hello.ts b/examples/nextjs-server-components/pages/api/hello.ts index 89e4d6bd..fd02e058 100644 --- a/examples/nextjs-server-components/pages/api/hello.ts +++ b/examples/nextjs-server-components/pages/api/hello.ts @@ -2,12 +2,9 @@ import type { NextApiRequest, NextApiResponse } from 'next'; type Data = { - name: string; + name: string; }; -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - res.status(200).json({ name: 'John Doe' }); +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({ name: 'John Doe' }); } diff --git a/examples/nextjs-server-components/utils/supabase-browser.ts b/examples/nextjs-server-components/utils/supabase-browser.ts index ce2eb459..5ae1ede8 100644 --- a/examples/nextjs-server-components/utils/supabase-browser.ts +++ b/examples/nextjs-server-components/utils/supabase-browser.ts @@ -1,5 +1,4 @@ import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'; import { Database } from '../db_types'; -export const createBrowserClient = () => - createBrowserSupabaseClient(); +export const createBrowserClient = () => createBrowserSupabaseClient(); diff --git a/examples/nextjs-server-components/utils/supabase-server.ts b/examples/nextjs-server-components/utils/supabase-server.ts index 76d14d2f..af895cf8 100644 --- a/examples/nextjs-server-components/utils/supabase-server.ts +++ b/examples/nextjs-server-components/utils/supabase-server.ts @@ -3,7 +3,7 @@ import { createServerComponentSupabaseClient } from '@supabase/auth-helpers-next import { Database } from '../db_types'; export const createServerClient = () => - createServerComponentSupabaseClient({ - headers, - cookies - }); + createServerComponentSupabaseClient({ + headers, + cookies + }); diff --git a/examples/nextjs/db_types.ts b/examples/nextjs/db_types.ts index 65bc3356..aedb3534 100644 --- a/examples/nextjs/db_types.ts +++ b/examples/nextjs/db_types.ts @@ -1,66 +1,60 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json } - | Json[]; +export type Json = string | number | boolean | null | { [key: string]: Json } | Json[]; export interface Database { - public: { - Tables: { - test: { - Row: { - created_at: string | null; - id: number; - }; - Insert: { - created_at?: string | null; - id?: number; - }; - Update: { - created_at?: string | null; - id?: number; - }; - }; - users: { - Row: { - city: string | null; - country: string | null; - created_at: string; - full_name: string | null; - id: string; - username: string | null; - }; - Insert: { - city?: string | null; - country?: string | null; - created_at?: string; - full_name?: string | null; - id: string; - username?: string | null; - }; - Update: { - city?: string | null; - country?: string | null; - created_at?: string; - full_name?: string | null; - id?: string; - username?: string | null; - }; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - [_ in never]: never; - }; - Enums: { - [_ in never]: never; - }; - CompositeTypes: { - [_ in never]: never; - }; - }; + public: { + Tables: { + test: { + Row: { + created_at: string | null; + id: number; + }; + Insert: { + created_at?: string | null; + id?: number; + }; + Update: { + created_at?: string | null; + id?: number; + }; + }; + users: { + Row: { + city: string | null; + country: string | null; + created_at: string; + full_name: string | null; + id: string; + username: string | null; + }; + Insert: { + city?: string | null; + country?: string | null; + created_at?: string; + full_name?: string | null; + id: string; + username?: string | null; + }; + Update: { + city?: string | null; + country?: string | null; + created_at?: string; + full_name?: string | null; + id?: string; + username?: string | null; + }; + }; + }; + Views: { + [_ in never]: never; + }; + Functions: { + [_ in never]: never; + }; + Enums: { + [_ in never]: never; + }; + CompositeTypes: { + [_ in never]: never; + }; + }; } diff --git a/examples/nextjs/middleware.ts b/examples/nextjs/middleware.ts index 9433cfce..be0491ef 100644 --- a/examples/nextjs/middleware.ts +++ b/examples/nextjs/middleware.ts @@ -3,28 +3,28 @@ 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 = createMiddlewareSupabaseClient({ req, res }); - // Check if we have a session - const { - data: { session } - } = await supabase.auth.getSession(); + // 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; - } + // 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); + // 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' + matcher: '/middleware-protected' }; diff --git a/examples/nextjs/pages/_app.tsx b/examples/nextjs/pages/_app.tsx index 5f0d6567..9eaf4a2c 100644 --- a/examples/nextjs/pages/_app.tsx +++ b/examples/nextjs/pages/_app.tsx @@ -1,40 +1,32 @@ import { useRouter } from 'next/router'; -import { - createBrowserSupabaseClient, - Session -} from '@supabase/auth-helpers-nextjs'; +import { createBrowserSupabaseClient, Session } from '@supabase/auth-helpers-nextjs'; import { SessionContextProvider } from '@supabase/auth-helpers-react'; import type { AppProps } from 'next/app'; import { useState } from 'react'; import { Database } from '../db_types'; import '../styles/globals.css'; -function MyApp({ - Component, - pageProps -}: AppProps<{ initialSession: Session }>) { - const router = useRouter(); - const [supabaseClient] = useState(() => - createBrowserSupabaseClient() - ); +function MyApp({ Component, pageProps }: AppProps<{ initialSession: Session }>) { + const router = useRouter(); + const [supabaseClient] = useState(() => createBrowserSupabaseClient()); - return ( - - + return ( + + - - - ); + + + ); } export default MyApp; diff --git a/examples/nextjs/pages/api/callback.ts b/examples/nextjs/pages/api/callback.ts index 3b19c528..048c6b9d 100644 --- a/examples/nextjs/pages/api/callback.ts +++ b/examples/nextjs/pages/api/callback.ts @@ -3,16 +3,16 @@ import { NextApiHandler } from 'next'; import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'; const callback: NextApiHandler = async (req, res) => { - // Create authenticated Supabase Client - const supabase = createServerSupabaseClient({ req, res }); + // Create authenticated Supabase Client + const supabase = createServerSupabaseClient({ req, res }); - const code = req.query.code; + const code = req.query.code; - if (typeof code === 'string') { - await supabase.auth.exchangeCodeForSession(code); - } + if (typeof code === 'string') { + await supabase.auth.exchangeCodeForSession(code); + } - res.redirect('/'); + res.redirect('/'); }; export default callback; diff --git a/examples/nextjs/pages/api/protected-route.ts b/examples/nextjs/pages/api/protected-route.ts index 7e97a2f9..4bf899c1 100644 --- a/examples/nextjs/pages/api/protected-route.ts +++ b/examples/nextjs/pages/api/protected-route.ts @@ -3,23 +3,22 @@ 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(); + // 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' - }); + 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('users').select('*'); - res.json(data); + // Run queries with RLS on the server + const { data } = await supabase.from('users').select('*'); + res.json(data); }; export default ProtectedRoute; diff --git a/examples/nextjs/pages/github-provider-token.tsx b/examples/nextjs/pages/github-provider-token.tsx index 0a531c31..4814066d 100644 --- a/examples/nextjs/pages/github-provider-token.tsx +++ b/examples/nextjs/pages/github-provider-token.tsx @@ -1,61 +1,51 @@ // pages/protected-page.js -import { - User, - createServerSupabaseClient -} from '@supabase/auth-helpers-nextjs'; +import { User, createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'; import { GetServerSidePropsContext } from 'next'; import Link from 'next/link'; -export default function ProtectedPage({ - user, - allRepos -}: { - user: User; - allRepos: any; -}) { - return ( - <> -

- [Home] | [ - withPageAuth] -

-
Protected content for {user.email}
-

Data fetched with provider token:

-
{JSON.stringify(allRepos, null, 2)}
-

user:

-
{JSON.stringify(user, null, 2)}
- - ); +export default function ProtectedPage({ user, allRepos }: { user: User; allRepos: any }) { + return ( + <> +

+ [Home] | [withPageAuth] +

+
Protected content for {user.email}
+

Data fetched with provider token:

+
{JSON.stringify(allRepos, null, 2)}
+

user:

+
{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(); + // 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 - } - }; + 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; + // 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(); + 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 } }; + return { props: { user, allRepos } }; }; diff --git a/examples/nextjs/pages/index.tsx b/examples/nextjs/pages/index.tsx index a2ab493f..299cc6e1 100644 --- a/examples/nextjs/pages/index.tsx +++ b/examples/nextjs/pages/index.tsx @@ -1,8 +1,4 @@ -import { - useSessionContext, - useSupabaseClient, - useUser -} from '@supabase/auth-helpers-react'; +import { useSessionContext, useSupabaseClient, useUser } from '@supabase/auth-helpers-react'; import { Auth, ThemeSupa } from '@supabase/auth-ui-react'; import type { NextPage } from 'next'; import Link from 'next/link'; @@ -10,74 +6,72 @@ import { useEffect, useState } from 'react'; import { Database } from '../db_types'; const LoginPage: NextPage = () => { - const { isLoading, session, error } = useSessionContext(); - const user = useUser(); - const supabaseClient = useSupabaseClient(); + const { isLoading, session, error } = useSessionContext(); + const user = useUser(); + const supabaseClient = useSupabaseClient(); - const [data, setData] = useState(null); + const [data, setData] = useState(null); - useEffect(() => { - async function loadData() { - const { data } = await supabaseClient.from('users').select('*').single(); - setData(data); - } + useEffect(() => { + async function loadData() { + const { data } = await supabaseClient.from('users').select('*').single(); + setData(data); + } - if (user) loadData(); - }, [user, supabaseClient]); + if (user) loadData(); + }, [user, supabaseClient]); - if (!session) - return ( - <> - {error &&

{error.message}

} - {isLoading ?

Loading...

:

Loaded!

} - - - - ); + if (!session) + return ( + <> + {error &&

{error.message}

} + {isLoading ?

Loading...

:

Loaded!

} + + + + ); - return ( - <> -

- [getServerSideProps] | [ - server-side RLS] |{' '} - - -

- {isLoading ?

Loading...

:

Loaded!

} -

user:

-
{JSON.stringify(session, null, 2)}
-

client-side data fetching with RLS

-
{JSON.stringify(data, null, 2)}
- - ); + return ( + <> +

+ [getServerSideProps] | [ + server-side RLS] |{' '} + + +

+ {isLoading ?

Loading...

:

Loaded!

} +

user:

+
{JSON.stringify(session, null, 2)}
+

client-side data fetching with RLS

+
{JSON.stringify(data, null, 2)}
+ + ); }; export default LoginPage; diff --git a/examples/nextjs/pages/profile.tsx b/examples/nextjs/pages/profile.tsx index a7ebd23d..dc0926eb 100644 --- a/examples/nextjs/pages/profile.tsx +++ b/examples/nextjs/pages/profile.tsx @@ -1,44 +1,40 @@ // pages/profile.js -import { - createServerSupabaseClient, - User -} from '@supabase/auth-helpers-nextjs'; +import { createServerSupabaseClient, User } from '@supabase/auth-helpers-nextjs'; import { GetServerSidePropsContext } from 'next'; import Link from 'next/link'; export default function Profile({ user }: { user: User }) { - return ( - <> -

- [Home] | [ - server-side RLS] -

-
Hello {user.email}
-
{JSON.stringify(user, null, 2)}
- - ); + return ( + <> +

+ [Home] | [server-side RLS] +

+
Hello {user.email}
+
{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(); + // 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 - } - }; + if (!session) + return { + redirect: { + destination: '/', + permanent: false + } + }; - return { - props: { - initialSession: session, - user: session.user - } - }; + return { + props: { + initialSession: session, + user: session.user + } + }; }; diff --git a/examples/nextjs/pages/protected-page.tsx b/examples/nextjs/pages/protected-page.tsx index f1f80c9c..1b8952c4 100644 --- a/examples/nextjs/pages/protected-page.tsx +++ b/examples/nextjs/pages/protected-page.tsx @@ -1,57 +1,47 @@ // pages/protected-page.js -import { - createServerSupabaseClient, - User -} from '@supabase/auth-helpers-nextjs'; +import { createServerSupabaseClient, User } from '@supabase/auth-helpers-nextjs'; import { GetServerSidePropsContext } from 'next'; import Link from 'next/link'; -export default function ProtectedPage({ - user, - data -}: { - user: User; - data: any; -}) { - return ( - <> -

- [Home] | [ - getServerSideProps] -

-
Protected content for {user?.email}
-

server-side fetched data with RLS:

-
{JSON.stringify(data, null, 2)}
-

user:

-
{JSON.stringify(user, null, 2)}
- - ); +export default function ProtectedPage({ user, data }: { user: User; data: any }) { + return ( + <> +

+ [Home] | [getServerSideProps] +

+
Protected content for {user?.email}
+

server-side fetched data with RLS:

+
{JSON.stringify(data, null, 2)}
+

user:

+
{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(); + // 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 - } - }; + if (!session) + return { + redirect: { + destination: '/', + permanent: false + } + }; - // Run queries with RLS on the server - const { data } = await supabase.from('users').select('*'); + // Run queries with RLS on the server + const { data } = await supabase.from('users').select('*'); - return { - props: { - initialSession: session, - user: session.user, - data: data ?? [] - } - }; + return { + props: { + initialSession: session, + user: session.user, + data: data ?? [] + } + }; }; diff --git a/examples/remix/app/entry.client.tsx b/examples/remix/app/entry.client.tsx index ea516524..ee296542 100644 --- a/examples/remix/app/entry.client.tsx +++ b/examples/remix/app/entry.client.tsx @@ -3,20 +3,20 @@ import { startTransition, StrictMode } from 'react'; import { hydrateRoot } from 'react-dom/client'; function hydrate() { - startTransition(() => { - hydrateRoot( - document, - - - - ); - }); + startTransition(() => { + hydrateRoot( + document, + + + + ); + }); } if (window.requestIdleCallback) { - window.requestIdleCallback(hydrate); + window.requestIdleCallback(hydrate); } else { - // Safari doesn't support requestIdleCallback - // https://caniuse.com/requestidlecallback - window.setTimeout(hydrate, 1); + // Safari doesn't support requestIdleCallback + // https://caniuse.com/requestidlecallback + window.setTimeout(hydrate, 1); } diff --git a/examples/remix/app/entry.server.tsx b/examples/remix/app/entry.server.tsx index dc1f831f..9a800b60 100644 --- a/examples/remix/app/entry.server.tsx +++ b/examples/remix/app/entry.server.tsx @@ -8,104 +8,94 @@ import { renderToPipeableStream } from 'react-dom/server'; const ABORT_DELAY = 5000; export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext ) { - return isbot(request.headers.get('user-agent')) - ? handleBotRequest( - request, - responseStatusCode, - responseHeaders, - remixContext - ) - : handleBrowserRequest( - request, - responseStatusCode, - responseHeaders, - remixContext - ); + return isbot(request.headers.get('user-agent')) + ? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext) + : handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext); } function handleBotRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext ) { - return new Promise((resolve, reject) => { - let didError = false; - - const { pipe, abort } = renderToPipeableStream( - , - { - onAllReady() { - const body = new PassThrough(); - - responseHeaders.set('Content-Type', 'text/html'); - - resolve( - new Response(body, { - headers: responseHeaders, - status: didError ? 500 : responseStatusCode - }) - ); - - pipe(body); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - didError = true; - - console.error(error); - } - } - ); - - setTimeout(abort, ABORT_DELAY); - }); + return new Promise((resolve, reject) => { + let didError = false; + + const { pipe, abort } = renderToPipeableStream( + , + { + onAllReady() { + const body = new PassThrough(); + + responseHeaders.set('Content-Type', 'text/html'); + + resolve( + new Response(body, { + headers: responseHeaders, + status: didError ? 500 : responseStatusCode + }) + ); + + pipe(body); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + didError = true; + + console.error(error); + } + } + ); + + setTimeout(abort, ABORT_DELAY); + }); } function handleBrowserRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - remixContext: EntryContext + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext ) { - return new Promise((resolve, reject) => { - let didError = false; - - const { pipe, abort } = renderToPipeableStream( - , - { - onShellReady() { - const body = new PassThrough(); - - responseHeaders.set('Content-Type', 'text/html'); - - resolve( - new Response(body, { - headers: responseHeaders, - status: didError ? 500 : responseStatusCode - }) - ); - - pipe(body); - }, - onShellError(err: unknown) { - reject(err); - }, - onError(error: unknown) { - didError = true; - - console.error(error); - } - } - ); - - setTimeout(abort, ABORT_DELAY); - }); + return new Promise((resolve, reject) => { + let didError = false; + + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + const body = new PassThrough(); + + responseHeaders.set('Content-Type', 'text/html'); + + resolve( + new Response(body, { + headers: responseHeaders, + status: didError ? 500 : responseStatusCode + }) + ); + + pipe(body); + }, + onShellError(err: unknown) { + reject(err); + }, + onError(error: unknown) { + didError = true; + + console.error(error); + } + } + ); + + setTimeout(abort, ABORT_DELAY); + }); } diff --git a/examples/remix/app/root.tsx b/examples/remix/app/root.tsx index 66c0e2ff..1d44bd34 100644 --- a/examples/remix/app/root.tsx +++ b/examples/remix/app/root.tsx @@ -1,32 +1,25 @@ import { MetaFunction } from '@remix-run/node'; -import { - Links, - LiveReload, - Meta, - Outlet, - Scripts, - ScrollRestoration -} from '@remix-run/react'; +import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; export const meta: MetaFunction = () => ({ - charset: 'utf-8', - title: 'New Remix App', - viewport: 'width=device-width,initial-scale=1' + charset: 'utf-8', + title: 'New Remix App', + viewport: 'width=device-width,initial-scale=1' }); export default function App() { - return ( - - - - - - - - - - - - - ); + return ( + + + + + + + + + + + + + ); } diff --git a/examples/remix/app/routes/__supabase.tsx b/examples/remix/app/routes/__supabase.tsx index d26096b1..1311a9ad 100644 --- a/examples/remix/app/routes/__supabase.tsx +++ b/examples/remix/app/routes/__supabase.tsx @@ -14,8 +14,8 @@ export type TypedSupabaseClient = SupabaseClient; export type MaybeSession = Session | null; export type SupabaseContext = { - supabase: TypedSupabaseClient; - session: MaybeSession; + supabase: TypedSupabaseClient; + session: MaybeSession; }; // this uses Pathless Layout Routes [1] to wrap up all our Supabase logic @@ -23,76 +23,73 @@ export type SupabaseContext = { // [1] https://remix.run/docs/en/v1/guides/routing#pathless-layout-routes export const loader = async ({ request }: LoaderArgs) => { - // environment variables may be stored somewhere other than - // `process.env` in runtimes other than node - // we need to pipe these Supabase environment variables to the browser - const env = { - SUPABASE_URL: process.env.SUPABASE_URL!, - SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY! - }; - - // We can retrieve the session on the server and hand it to the client. - // This is used to make sure the session is available immediately upon rendering - const response = new Response(); - - const supabase = createServerClient({ request, response }); - - const { - data: { session } - } = await supabase.auth.getSession(); - - // in order for the set-cookie header to be set, - // headers must be returned as part of the loader response - return json( - { - env, - session - }, - { - headers: response.headers - } - ); + // environment variables may be stored somewhere other than + // `process.env` in runtimes other than node + // we need to pipe these Supabase environment variables to the browser + const env = { + SUPABASE_URL: process.env.SUPABASE_URL!, + SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY! + }; + + // We can retrieve the session on the server and hand it to the client. + // This is used to make sure the session is available immediately upon rendering + const response = new Response(); + + const supabase = createServerClient({ request, response }); + + const { + data: { session } + } = await supabase.auth.getSession(); + + // in order for the set-cookie header to be set, + // headers must be returned as part of the loader response + return json( + { + env, + session + }, + { + headers: response.headers + } + ); }; export default function Supabase() { - const { env, session } = useLoaderData(); - const fetcher = useFetcher(); - - // it is important to create a single instance of Supabase - // to use across client components - outlet context 👇 - const [supabase] = useState(() => - createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY) - ); - - const serverAccessToken = session?.access_token; - - useEffect(() => { - const { - data: { subscription } - } = supabase.auth.onAuthStateChange((event, session) => { - if ( - session?.access_token !== serverAccessToken && - fetcher.state === 'idle' - ) { - // server and client are out of sync. - // Remix recalls active loaders after actions complete - fetcher.submit(null, { - method: 'post', - action: '/handle-supabase-auth' - }); - } - }); - - return () => { - subscription.unsubscribe(); - }; - }, [serverAccessToken, supabase, fetcher]); - - return ( - <> - -