As you update your database the changes are immediately accessible through your API. - **Self documenting.**
Supabase generates documentation in the Dashboard which updates as you make database changes. @@ -33,23 +37,8 @@ It provides everything you need from a CRUD API: - Works with Postgres Functions - Works with the Postgres security model - including Row Level Security, Roles, and Grants. -
{JSON.stringify({ data }, null, 2)}
-}
-```
-
-### Client-side
-
-We still need a Supabase instance client-side for authentication and realtime subscriptions. It is important, when using Supabase client-side, to have a single instance of a client. We can share this singleton instance across our components using providers and React context.
+All Client Components need to share a single instance of the Supabase client. We can wrap our application in a `{JSON.stringify(posts, null, 2)}
-}
-```
-
-This can now be used in a Server Component to subscribe to realtime updates.
-
-Create a new file at `/app/realtime/page.jsx` and populate with the following:
-
-```jsx title="/app/realtime/page.jsx"
-import 'server-only'
-
-import { createClient } from '../../utils/supabase-server'
-import Posts from './posts'
+```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 Realtime() {
- const supabase = createClient()
+export default async function ServerComponent() {
+ const supabase = createServerComponentSupabaseClient({
+ headers,
+ cookies,
+ })
const { data } = await supabase.from('posts').select('*')
- return {JSON.stringify(data, null, 2)}
}
```
@@ -641,69 +399,91 @@ export default async function Realtime() {
{JSON.stringify(posts, null, 2)}
-}
-```
-
-> TypeScript types can be [generated with the Supabase CLI](https://supabase.com/docs/reference/javascript/typescript-support) and passed to `createServerSupabaseClient` to add type support to the Supabase client.
-
-This can now be used in a Server Component to subscribe to realtime updates.
-
-Create a new file at `/app/realtime/page.tsx` and populate with the following:
-
-```tsx title="/app/realtime/page.tsx"
-import 'server-only'
-
-import { createClient } from '../../utils/supabase-server'
-import Posts from './posts'
+import type { Database } from '@/lib/database.types'
// do not cache this page
export const revalidate = 0
-export default async function Realtime() {
- const supabase = createClient()
+export default async function ServerComponent() {
+ const supabase = createServerComponentSupabaseClient{JSON.stringify(data, null, 2)}
}
```
client-side data fetching with RLS
+{JSON.stringify(loadedData, null, 2)}
+{/if}
+```
+
+## Server-side data fetching with RLS
+
+```html
+
+
+
+{JSON.stringify(tableData, null, 2)}
+{JSON.stringify(user, null, 2)}
+```
+
+```ts
+// src/routes/profile/+page.ts
+import type { PageLoad } from './$types';
+import { redirect } from '@sveltejs/kit';
+
+export const load: PageLoad = async ({ parent }) => {
+ const { supabase, session } = await parent();
+ if (!session) {
+ throw redirect(303, '/');
+ }
+ const { data: tableData } = await supabase.from('test').select('*');
+
+ return {
+ user: session.user,
+ tableData
+ };
+};
+```
+
+## Protecting API routes
+
+Wrap an API Route to check that the user has a valid session. If they're not logged in the session is `null`.
+
+```ts
+// src/routes/api/protected-route/+server.ts
+import type { RequestHandler } from './$types';
+import { json, error } from '@sveltejs/kit';
+
+export const GET: RequestHandler = async ({
+ locals: { supabase, getSession }
+}) => {
+ const session = await getSession();
+ if (!session) {
+ // the user is not signed in
+ throw error(401, { message: 'Unauthorized' });
+ }
+ const { data } = await supabase.from('test').select('*');
+
+ return json({ data });
+};
+```
+
+If you visit `/api/protected-route` without a valid session cookie, you will get a 401 response.
+
+## Protecting Actions
+
+Wrap an Action to check that the user has a valid session. If they're not logged in the session is `null`.
+
+```ts
+// src/routes/posts/+page.server.ts
+import type { Actions } from './$types';
+import { error, fail } from '@sveltejs/kit';
+
+export const actions: Actions = {
+ createPost: async ({ request, locals: { supabase, getSession } }) => {
+ const session = await getSession();
+
+ if (!session) {
+ // the user is not signed in
+ throw error(401, { message: 'Unauthorized' });
+ }
+ // we are save, let the user create the post
+ const formData = await request.formData();
+ const content = formData.get('content');
+
+ const { error: createPostError, data: newPost } = await supabase
+ .from('posts')
+ .insert({ content });
+
+ if (createPostError) {
+ return fail(500, {
+ supabaseErrorMessage: createPostError.message
+ });
+ }
+ return {
+ newPost
+ };
+ }
+};
+```
+
+If you try to submit a form with the action `?/createPost` without a valid session cookie, you will get a 401 error response.
+
+## Saving and deleting the session
+
+```ts
+import type { Actions } from './$types';
+import { fail, redirect } from '@sveltejs/kit';
+import { AuthApiError } from '@supabase/supabase-js';
+
+export const actions: Actions = {
+ signin: async ({ request, locals: { supabase } }) => {
+ const formData = await request.formData();
+
+ const email = formData.get('email') as string;
+ const password = formData.get('password') as string;
+
+ const { error } = await supabase.auth.signInWithPassword({
+ email,
+ password
+ });
+
+ if (error) {
+ if (error instanceof AuthApiError && error.status === 400) {
+ return fail(400, {
+ error: 'Invalid credentials.',
+ values: {
+ email
+ }
+ });
+ }
+ return fail(500, {
+ error: 'Server error. Try again later.',
+ values: {
+ email
+ }
+ });
+ }
+
+ throw redirect(303, '/dashboard');
+ },
+
+ signout: async ({ locals: { supabase } }) => {
+ await supabase.auth.signOut();
+ throw redirect(303, '/');
+ }
+};
+```
+
+## Protecting multiple routes
+
+To avoid writing the same auth logic in every single route you can use the handle hook to
+protect multiple routes at once.
+
+```ts
+// src/hooks.server.ts
+import type { RequestHandler } from './$types';
+import { getSupabase } from '@supabase/auth-helpers-sveltekit';
+import { redirect, error } from '@sveltejs/kit';
+
+export const handle: Handle = async ({ event, resolve }) => {
+ // protect requests to all routes that start with /protected-routes
+ if (event.url.pathname.startsWith('/protected-routes')) {
+ const session = await event.locals.getSession();
+ if (!session) {
+ // the user is not signed in
+ throw redirect(303, '/');
+ }
+ }
+
+ // protect POST requests to all routes that start with /protected-posts
+ if (
+ event.url.pathname.startsWith('/protected-posts') &&
+ event.request.method === 'POST'
+ ) {
+ const session = await event.locals.getSession();
+ if (!session) {
+ // the user is not signed in
+ throw error(303, '/');
+ }
+ }
+
+ return resolve(event);
+};
+```
+
+## Migrate from 0.8.x to 0.9 [#migration]
+
+### Set up the Supabase client [#migration-set-up-supabase-client]
+
+In version 0.9 we now setup our Supabase client for the server inside of a `hooks.server.ts` file.
+
+I am not logged in
-{:else} -Welcome {$page.data.session.user.email}
-I am logged in!
-{/if} +declare global { + namespace App { + interface Locals { + supabase: SupabaseClientclient-side data fetching with RLS
-{JSON.stringify(loadedData, null, 2)}
-{/if}
-```
-
-## Server-side data fetching with RLS
+{JSON.stringify(tableData, null, 2)}
+{JSON.stringify(user, null, 2)}
+```
+
+```ts title=src/routes/profile/+page.ts
+// src/routes/profile/+page.ts
+import type { PageLoad } from './$types';
+import { redirect } from '@sveltejs/kit';
+
+export const load: PageLoad = async ({ parent }) => {
+ const { supabase, session } = await parent();
+ if (!session) {
+ throw redirect(303, '/');
+ }
+ const { data: tableData } = await supabase.from('test').select('*');
+
+ return {
+ user: session.user,
+ tableData
+ };
+};
+```
+
+
+
-
+
+