---
id: 'sveltekit'
title: 'Supabase Auth with SvelteKit'
description: 'Convenience helpers for implementing user authentication in SvelteKit.'
sidebar_label: 'SvelteKit'
sitemapPriority: 0.5
---

<Admonition type="caution">

We generally recommend using the new `@supabase/ssr` package instead of `auth-helpers`. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more.

</Admonition>

<Accordion
  type="default"
  openBehaviour="multiple"
  chevronAlign="right"
  justified
  size="medium"
  className="text-foreground-light border-b mt-8 pb-2"
  >

  <AccordionItem
        header="See legacy docs"
        id="legacy-docs"
      >

This submodule provides convenience helpers for implementing user authentication in [SvelteKit](https://kit.svelte.dev/) applications.

## Configuration

### Install SvelteKit Auth helpers library

This library supports Node.js `^16.15.0`.

```sh Terminal
npm install @supabase/auth-helpers-sveltekit @supabase/supabase-js
```

### Declare environment variables

Retrieve your project's URL and anon key from your [API settings](/dashboard/project/_/settings/api), and create a `.env.local` file with the following environment variables:

```bash .env.local
# Find these in your Supabase project settings https://supabase.com/dashboard/project/_/settings/api
PUBLIC_SUPABASE_URL=https://your-project.supabase.co
PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_... or anon key
```

### Creating a Supabase client

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

Create a new `hooks.server.js` file in the root of your project and populate with the following to retrieve the user session.

<$Partial path="get_session_warning.mdx" />

```js src/hooks.server.js
// src/hooks.server.js
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'

export const handle = async ({ event, resolve }) => {
  event.locals.supabase = createSupabaseServerClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event,
  })

  /**
   * Unlike `supabase.auth.getSession`, which is unsafe on the server because it
   * doesn't validate the JWT, this function validates the JWT by first calling
   * `getUser` and aborts early if the JWT signature is invalid.
   */
  event.locals.safeGetSession = async () => {
    const {
      data: { user },
      error,
    } = await supabase.auth.getUser()
    if (error) {
      return { session: null, user: null }
    }

    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession()
    return { session, user }
  }

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      return name === 'content-range' || name === 'x-supabase-api-version'
    },
  })
}
```

</TabPanel>

<TabPanel id="ts" label="TypeScript">

Create a new `hooks.server.ts` file in the root of your project and populate with the following:

```ts src/hooks.server.ts
// src/hooks.server.ts
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
import type { Handle } from '@sveltejs/kit'

export const handle: Handle = async ({ event, resolve }) => {
  event.locals.supabase = createSupabaseServerClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event,
  })

  /**
   * Unlike `supabase.auth.getSession`, which is unsafe on the server because it
   * doesn't validate the JWT, this function validates the JWT by first calling
   * `getUser` and aborts early if the JWT signature is invalid.
   */
  event.locals.safeGetSession = async () => {
    const {
      data: { user },
      error,
    } = await supabase.auth.getUser()
    if (error) {
      return { session: null, user: null }
    }

    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession()
    return { session, user }
  }

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      return name === 'content-range' || name === 'x-supabase-api-version'
    },
  })
}
```

</TabPanel>
</Tabs>

<Admonition type="note">

Note that we are specifying `filterSerializedResponseHeaders` here. We need to tell SvelteKit that Supabase needs the `content-range` and `x-supabase-api-version` headers.

</Admonition>

### Code Exchange route

The `Code Exchange` route is required for the [server-side auth flow](/docs/guides/auth/server-side-rendering) implemented by the SvelteKit Auth Helpers. It exchanges an auth `code` for the user's `session`, which is set as a cookie for future requests made to Supabase.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

Create a new file at `src/routes/auth/callback/+server.js` and populate with the following:

```js src/routes/auth/callback/+server.js
import { redirect } from '@sveltejs/kit'

export const GET = async ({ url, locals: { supabase } }) => {
  const code = url.searchParams.get('code')

  if (code) {
    await supabase.auth.exchangeCodeForSession(code)
  }

  redirect(303, '/')
}
```

</TabPanel>

<TabPanel id="ts" label="TypeScript">

Create a new file at `src/routes/auth/callback/+server.ts` and populate with the following:

```ts src/routes/auth/callback/+server.ts
import { redirect } from '@sveltejs/kit'

export const GET = async ({ url, locals: { supabase } }) => {
  const code = url.searchParams.get('code')

  if (code) {
    await supabase.auth.exchangeCodeForSession(code)
  }

  redirect(303, '/')
}
```

</TabPanel>
</Tabs>

### Generate types from your database

In order to get the most out of TypeScript and its IntelliSense, you should import the generated Database types into the `app.d.ts` type definition file that comes with your SvelteKit project, where `import('./DatabaseDefinitions')` points to the generated types file outlined in [v2 docs here](/docs/reference/javascript/release-notes#typescript-support) after you have logged in, linked, and generated types through the Supabase CLI.

```ts src/app.d.ts
// src/app.d.ts

import { SupabaseClient, Session, User } from '@supabase/supabase-js'
import { Database } from './DatabaseDefinitions'

declare global {
  namespace App {
    interface Locals {
      supabase: SupabaseClient<Database>
      safeGetSession(): Promise<{ session: Session | null; user: User | null }>
    }
    interface PageData {
      session: Session | null
      user: User | null
    }
    // interface Error {}
    // interface Platform {}
  }
}
```

## Authentication

Authentication can be initiated [client](/docs/guides/auth/auth-helpers/sveltekit#client-side) or [server-side](/docs/guides/auth/auth-helpers/sveltekit#server-side). All of the [supabase-js authentication strategies](/docs/reference/javascript/auth-api) are supported with the Auth Helpers client.

<Admonition type="note">

Note: The authentication flow requires the [Code Exchange Route](/docs/guides/auth/auth-helpers/sveltekit#code-exchange-route) to exchange a `code` for the user's `session`.

</Admonition>

### Client-side

#### Send session to client

To make the session available across the UI, including pages and layouts, it is crucial to pass the session as a parameter in the root layout's server load function.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

```js src/routes/+layout.server.js
// src/routes/+layout.server.js
export const load = async ({ locals: { safeGetSession } }) => {
  const { session, user } = await safeGetSession()

  return {
    session,
    user,
  }
}
```

</TabPanel>
<TabPanel id="ts" label="TypeScript">

```ts src/routes/+layout.server.ts
// src/routes/+layout.server.ts
export const load = async ({ locals: { safeGetSession } }) => {
  const { session, user } = await safeGetSession()

  return {
    session,
    user,
  }
}
```

</TabPanel>
</Tabs>

#### Shared load functions and pages

To utilize Supabase in shared load functions and within pages, it is essential to create a Supabase client in the root layout load.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

```ts src/routes/+layout.js
// src/routes/+layout.js
import { PUBLIC_SUPABASE_PUBLISHABLE_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'

export const load = async ({ fetch, data, depends }) => {
  depends('supabase:auth')

  const supabase = createSupabaseLoadClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event: { fetch },
    serverSession: data.session,
  })

  /**
   * It's fine to use `getSession` here, because on the client, `getSession` is
   * safe, and on the server, it reads `session` from the `LayoutData`, which
   * safely checked the session using `safeGetSession`.
   */
  const {
    data: { session },
  } = await supabase.auth.getSession()

  return { supabase, session }
}
```

</TabPanel>

<TabPanel id="ts" label="TypeScript">

```ts src/routes/+layout.ts
// src/routes/+layout.ts
import { PUBLIC_SUPABASE_PUBLISHABLE_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
import type { Database } from '../DatabaseDefinitions'

export const load = async ({ fetch, data, depends }) => {
  depends('supabase:auth')

  const supabase = createSupabaseLoadClient<Database>({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event: { fetch },
    serverSession: data.session,
  })

  /**
   * It's fine to use `getSession` here, because on the client, `getSession` is
   * safe, and on the server, it reads `session` from the `LayoutData`, which
   * safely checked the session using `safeGetSession`.
   */
  const {
    data: { session },
  } = await supabase.auth.getSession()

  return { supabase, session }
}
```

<Admonition type="note">

TypeScript types can be [generated with the Supabase CLI](/docs/reference/javascript/typescript-support) and passed to `createSupabaseLoadClient` to add type support to the Supabase client.

</Admonition>

</TabPanel>
</Tabs>

Access the client inside pages by `$page.data.supabase` or `data.supabase` when using `export let data`.

The usage of `depends` tells SvelteKit that this load function should be executed whenever `invalidate` is called to keep the page store in sync.

`createSupabaseLoadClient` caches the client when running in a browser environment and therefore does not create a new client for every time the load function runs.

#### Setting up the event listener on the client side

We need to create an event listener in the root `+layout.svelte` file in order to catch Supabase events being triggered.

```svelte src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { invalidate } from '$app/navigation'
  import { onMount } from 'svelte'

  export let data

  let { supabase, session } = data
  $: ({ supabase, session } = data)

  onMount(() => {
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((event, _session) => {
      if (_session?.expires_at !== session?.expires_at) {
        invalidate('supabase:auth')
      }
    })

    return () => subscription.unsubscribe()
  });
</script>

<slot />
```

The usage of `invalidate` tells SvelteKit that the root `+layout.ts` load function should be executed whenever the session updates to keep the page store in sync.

#### Sign in / sign up / sign out

We can access the Supabase instance in our `+page.svelte` file through the data object.

```svelte src/routes/auth/+page.svelte
<!-- // src/routes/auth/+page.svelte -->
<script>
  export let data
  let { supabase } = data
  $: ({ supabase } = data)

  let email
  let password

  const handleSignUp = async () => {
    await supabase.auth.signUp({
      email,
      password,
      options: {
        emailRedirectTo: `${location.origin}/auth/callback`,
      },
    })
  }

  const handleSignIn = async () => {
    await supabase.auth.signInWithPassword({
      email,
      password,
    })
  }

  const handleSignOut = async () => {
    await supabase.auth.signOut()
  }
</script>

<form on:submit="{handleSignUp}">
  <input name="email" bind:value="{email}" />
  <input type="password" name="password" bind:value="{password}" />
  <button>Sign up</button>
</form>

<button on:click="{handleSignIn}">Sign in</button>
<button on:click="{handleSignOut}">Sign out</button>
```

### Server-side

[Form Actions](https://kit.svelte.dev/docs/form-actions) can be used to trigger the authentication process from form submissions.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

```js src/routes/login/+page.server.js
// src/routes/login/+page.server.js
import { fail } from '@sveltejs/kit'

export const actions = {
  default: async ({ request, url, locals: { supabase } }) => {
    const formData = await request.formData()
    const email = formData.get('email')
    const password = formData.get('password')

    const { error } = await supabase.auth.signUp({
      email,
      password,
      options: {
        emailRedirectTo: `${url.origin}/auth/callback`,
      },
    })

    if (error) {
      return fail(500, { message: 'Server error. Try again later.', success: false, email })
    }

    return {
      message: 'Please check your email for a magic link to log into the website.',
      success: true,
    }
  },
}
```

```svelte src/routes/login/+page.svelte
<!-- // src/routes/login/+page.svelte -->
<script>
	import { enhance } from '$app/forms'
	export let form
</script>

<form method="post" use:enhance>
  <input name="email" value={form?.email ?? ''} />
  <input type="password" name="password" />
  <button>Sign up</button>
</form>
```

</TabPanel>

<TabPanel id="ts" label="TypeScript">

```js src/routes/login/+page.server.ts
// src/routes/login/+page.server.ts
import { fail } from '@sveltejs/kit'

export const actions = {
  default: async ({ request, url, 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.signUp({
      email,
      password,
      options: {
        emailRedirectTo: `${url.origin}/auth/callback`,
      },
    })

    if (error) {
      return fail(500, { message: 'Server error. Try again later.', success: false, email })
    }

    return {
      message: 'Please check your email for a magic link to log into the website.',
      success: true,
    }
  },
}
```

```svelte src/routes/login/+page.svelte
<!-- // src/routes/login/+page.svelte -->
<script lang="ts">
	import { enhance } from '$app/forms'
	export let form
</script>

<form method="post" use:enhance>
  <input name="email" value={form?.email ?? ''} />
  <input type="password" name="password" />
  <button>Sign up</button>
</form>
```

</TabPanel>
</Tabs>

## Authorization

### 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
// src/routes/api/protected-route/+server.ts
import { json, error } from '@sveltejs/kit'

export const GET = async ({ locals: { supabase, safeGetSession } }) => {
  const { session } = await safeGetSession()
  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
// src/routes/posts/+page.server.ts
import { error, fail } from '@sveltejs/kit'

export const actions = {
  createPost: async ({ request, locals: { supabase, safeGetSession } }) => {
    const { session } = await safeGetSession()

    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.

### Protecting multiple routes

To avoid writing the same auth logic in every single route you can also use the handle hook to
protect multiple routes at once. For this to work with your Supabase session, you need to use
SvelteKit's [sequence helper](https://kit.svelte.dev/docs/modules#sveltejs-kit-hooks) function.
Edit your `/src/hooks.server.js` with the below:

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="js"
  queryGroup="language"
>
<TabPanel id="js" label="JavaScript">

```js src/hooks.server.js
// src/hooks.server.js
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
import { redirect, error } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'

async function supabase({ event, resolve }) {
  event.locals.supabase = createSupabaseServerClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event,
  })

  /**
   * Unlike `supabase.auth.getSession`, which is unsafe on the server because it
   * doesn't validate the JWT, this function validates the JWT by first calling
   * `getUser` and aborts early if the JWT signature is invalid.
   */
  event.locals.safeGetSession = async () => {
    const {
      data: { user },
      error,
    } = await event.locals.supabase.auth.getUser()
    if (error) return { session: null, user: null }

    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession()
    return { session, user }
  }

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      return name === 'content-range' || name === 'x-supabase-api-version'
    },
  })
}

async function authorization({ event, resolve }) {
  // protect requests to all routes that start with /protected-routes
  if (event.url.pathname.startsWith('/protected-routes') && event.request.method === 'GET') {
    const { session } = await event.locals.safeGetSession()
    if (!session) {
      // the user is not signed in
      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.safeGetSession()
    if (!session) {
      // the user is not signed in
      throw error(303, '/')
    }
  }

  return resolve(event)
}

export const handle = sequence(supabase, authorization)
```

</TabPanel>

<TabPanel id="ts" label="TypeScript">

```ts src/hooks.server.ts
// src/hooks.server.ts
import { type Handle, redirect, error } from '@sveltejs/kit'
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
import { sequence } from '@sveltejs/kit/hooks'

async function supabase({ event, resolve }) {
  event.locals.supabase = createSupabaseServerClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event,
  })

  /**
   * Unlike `supabase.auth.getSession`, which is unsafe on the server because it
   * doesn't validate the JWT, this function validates the JWT by first calling
   * `getUser` and aborts early if the JWT signature is invalid.
   */
  event.locals.safeGetSession = async () => {
    const {
      data: { user },
      error,
    } = await event.locals.supabase.auth.getUser()
    if (error) return { session: null, user: null }

    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession()
    return { session, user }
  }

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      return name === 'content-range' || name === 'x-supabase-api-version'
    },
  })
}

async function authorization({ event, resolve }) {
  // protect requests to all routes that start with /protected-routes
  if (event.url.pathname.startsWith('/protected-routes') && event.request.method === 'GET') {
    const { session } = await event.locals.safeGetSession()
    if (!session) {
      // the user is not signed in
      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.safeGetSession()
    if (!session) {
      // the user is not signed in
      throw error(303, '/')
    }
  }

  return resolve(event)
}

export const handle: Handle = sequence(supabase, authorization)
```

</TabPanel>
</Tabs>

## Data fetching

### Client-side data fetching with RLS

For [row level security](/docs/guides/database/postgres/row-level-security) to work properly when fetching data client-side, you need to use `supabaseClient` from `PageData` and only run your query once the session is defined client-side:

```svelte src/routes/+page.svelte
<script lang="ts">
  export let data

  let loadedData = []
  async function loadData() {
    const { data: result } = await data.supabase.from('test').select('*').limit(20)
    loadedData = result
  }

  $: if (data.session) {
    loadData()
  }
</script>

{#if data.session}
<p>client-side data fetching with RLS</p>
<pre>{JSON.stringify(loadedData, null, 2)}</pre>
{/if}
```

### Server-side data fetching with RLS

```svelte src/routes/profile/+page.svelte
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
  export let data

  let { user, tableData } = data
  $: ({ user, tableData } = data)
</script>

<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts src/routes/profile/+page.ts
// src/routes/profile/+page.ts
import { redirect } from '@sveltejs/kit'

export const load = async ({ parent }) => {
  const { supabase, session } = await parent()
  if (!session) {
    redirect(303, '/')
  }
  const { data: tableData } = await supabase.from('test').select('*')

  return {
    user: session.user,
    tableData,
  }
}
```

## Saving and deleting the session

```ts
import { fail, redirect } from '@sveltejs/kit'
import { AuthApiError } from '@supabase/supabase-js'

export const 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,
        },
      })
    }

    redirect(303, '/dashboard')
  },

  signout: async ({ locals: { supabase } }) => {
    await supabase.auth.signOut()
    redirect(303, '/')
  },
}
```

## Migration guide [#migration]

### Migrate to 0.10

#### PKCE Auth flow

Proof Key for Code Exchange (PKCE) is the new server-side auth flow implemented by the SvelteKit Auth Helpers. It requires a server endpoint for `/auth/callback` that exchanges an auth `code` for the user's `session`.

Check the [Code Exchange Route steps](/docs/guides/auth/auth-helpers/sveltekit#code-exchange-route) above to implement this server endpoint.

#### Authentication

For authentication methods that have a `redirectTo` or `emailRedirectTo`, this must be set to this new code exchange route handler - `/auth/callback`. This is an example with the `signUp` function:

```ts
await supabase.auth.signUp({
  email: 'valid.email@supabase.io',
  password: 'sup3rs3cur3',
  options: {
    emailRedirectTo: 'http://localhost:3000/auth/callback',
  },
})
```

### Migrate from 0.8.x to 0.9 [#migration-0-9]

#### 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.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0.8"
  queryGroup="migration-version"
>
<TabPanel id="older-0.8" label="0.8.x">

```js src/lib/db.ts
// src/lib/db.ts
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
// or use the static env

// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public';

export const supabaseClient = createClient(
  env.PUBLIC_SUPABASE_URL,
  env.PUBLIC_SUPABASE_PUBLISHABLE_KEY
)
```

</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">

```js src/hooks.server.ts
// src/hooks.server.ts
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
import type { Handle } from '@sveltejs/kit'

export const handle: Handle = async ({ event, resolve }) => {
  event.locals.supabase = createSupabaseServerClient({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event,
  })

  /**
   * Unlike `supabase.auth.getSession`, which is unsafe on the server because it
   * doesn't validate the JWT, this function validates the JWT by first calling
   * `getUser` and aborts early if the JWT signature is invalid.
   */
  event.locals.safeGetSession = async () => {
    const {
      data: { user },
      error,
    } = await event.locals.supabase.auth.getUser()
    if (error) return { session: null, user: null }

    const {
      data: { session },
    } = await event.locals.supabase.auth.getSession()
    return { session, user }
  }

  return resolve(event, {
    filterSerializedResponseHeaders(name) {
      return name === 'content-range' || name === 'x-supabase-api-version'
    },
  })
}
```

</TabPanel>
</Tabs>

#### Initialize the client [#migration-initialize-client]

In order to use the Supabase library in your client code you will need to setup a shared load function inside the root `+layout.ts` and create a `+layout.svelte` to handle our event listening for Auth events.

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0.8"
  queryGroup="migration-version"
>
<TabPanel id="older-0.8" label="0.8.x">

```svelte src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { supabaseClient } from '$lib/db'
  import { invalidate } from '$app/navigation'
  import { onMount } from 'svelte'

  onMount(() => {
    const {
      data: { subscription },
    } = supabaseClient.auth.onAuthStateChange(() => {
      invalidate('supabase:auth')
    })

    return () => {
      subscription.unsubscribe()
    }
  })
</script>

<slot />
```

</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">

```ts src/routes/+layout.ts
// src/routes/+layout.ts
import { invalidate } from '$app/navigation'
import { PUBLIC_SUPABASE_PUBLISHABLE_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
import { createSupabaseLoadClient } from '@supabase/auth-helpers-sveltekit'
import type { LayoutLoad } from './$types'
import type { Database } from '../DatabaseDefinitions'

export const load: LayoutLoad = async ({ fetch, data, depends }) => {
  depends('supabase:auth')

  const supabase = createSupabaseLoadClient<Database>({
    supabaseUrl: PUBLIC_SUPABASE_URL,
    supabaseKey: PUBLIC_SUPABASE_PUBLISHABLE_KEY,
    event: { fetch },
    serverSession: data.session,
  })

  const {
    data: { session },
  } = await supabase.auth.getSession()

  return { supabase, session }
}
```

```svelte src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { invalidate } from '$app/navigation';
  import { onMount } from 'svelte';
  import type { LayoutData } from './$types';

  export let data: LayoutData;

  $: ({ supabase, session } = data);

  onMount(() => {
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((event, _session) => {
      if (_session?.expires_at !== session?.expires_at) {
        invalidate('supabase:auth')
      }
    });

    return () => subscription.unsubscribe();
  });
</script>

<slot />
```

</TabPanel>
</Tabs>

#### Set up hooks [#migration-set-up-hooks]

Since version 0.9 relies on `hooks.server.ts` to setup our client, we no longer need the `hooks.client.ts` in our project for Supabase related code.

#### Types [#migration-typings]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-8"
  queryGroup="migration-version"
>
<TabPanel id="older-0-8" label="0.8.x">

```ts src/app.d.ts
// src/app.d.ts
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
  interface Supabase {
    Database: import('./DatabaseDefinitions').Database
    SchemaName: 'public'
  }

  // interface Locals {}
  interface PageData {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }
  // interface Error {}
  // interface Platform {}
}
```

</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">

```ts src/app.d.ts
// src/app.d.ts
import { SupabaseClient, Session, User } from '@supabase/supabase-js'
import { Database } from './DatabaseDefinitions'

declare global {
  namespace App {
    interface Locals {
      supabase: SupabaseClient<Database>
      safeGetSession(): Promise<{ session: Session | null; user: User | null }>
    }
    interface PageData {
      session: Session | null
      user: User | null
    }
    // interface Error {}
    // interface Platform {}
  }
}
```

</TabPanel>
</Tabs>

#### Protecting a page [#migration-protecting-a-page]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-8"
  queryGroup="migration-version"
>
<TabPanel id="older-0-8" label="0.8.x">

```svelte src/routes/profile/+page.svelte
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
  /** @type {import('./$types').PageData} */
  export let data
  $: ({ user, tableData } = data)
</script>

<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts src/routes/profile/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'

export const load: PageLoad = async (event) => {
  const { session, supabaseClient } = await getSupabase(event)
  if (!session) {
    redirect(303, '/')
  }
  const { data: tableData } = await supabaseClient.from('test').select('*')

  return {
    user: session.user,
    tableData,
  }
}
```

</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">

```svelte src/routes/profile/+page.svelte
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
  import type { PageData } from './$types'

  export let data: PageData
  $: ({ user, tableData } = data)
</script>

<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts 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) {
    redirect(303, '/')
  }
  const { data: tableData } = await supabase.from('test').select('*')

  return {
    user: session.user,
    tableData,
  }
}
```

</TabPanel>
</Tabs>

#### Protecting a API route [#migration-protecting-a-api-route]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-8"
  queryGroup="migration-version"
>
<TabPanel id="older-0-8" label="0.8.x">

```ts src/routes/api/protected-route/+server.ts
// src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'

export const GET: RequestHandler = async (event) => {
  const { session, supabaseClient } = await getSupabase(event)
  if (!session) {
    redirect(303, '/')
  }
  const { data } = await supabaseClient.from('test').select('*')

  return json({ data })
}
```

</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">

```ts src/routes/api/protected-route/+server.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 })
}
```

</TabPanel>
</Tabs>

### Migrate from 0.7.x to 0.8 [#migration-0-8]

#### Set up the Supabase client [#migration-set-up-supabase-client-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0.7"
  queryGroup="migration-version"
>
<TabPanel id="older-0.7" label="0.7.x">

```js src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
import { env } from '$env/dynamic/public'
// or use the static env

// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public';

export const supabaseClient = createClient(
  env.PUBLIC_SUPABASE_URL,
  env.PUBLIC_SUPABASE_PUBLISHABLE_KEY,
  {
    persistSession: false,
    autoRefreshToken: false,
  }
)

setupSupabaseHelpers({
  supabaseClient,
  cookieOptions: {
    secure: !dev,
  },
})
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```js src/lib/db.ts
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
// or use the static env

// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public';

export const supabaseClient = createClient(
  env.PUBLIC_SUPABASE_URL,
  env.PUBLIC_SUPABASE_PUBLISHABLE_KEY
)
```

</TabPanel>
</Tabs>

#### Initialize the client [#migration-initialize-client-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0.7"
  queryGroup="migration-version"
>
<TabPanel id="older-0.7" label="0.7.x">

```svelte src/routes/+layout.svelte
<script lang="ts">
  // make sure the supabase instance is initialized on the client
  import '$lib/db'
  import { startSupabaseSessionSync } from '@supabase/auth-helpers-sveltekit'
  import { page } from '$app/stores'
  import { invalidateAll } from '$app/navigation'

  // this sets up automatic token refreshing
  startSupabaseSessionSync({
    page,
    handleRefresh: () => invalidateAll(),
  })
</script>

<slot />
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```svelte src/routes/+layout.svelte
<script>
  import { supabaseClient } from '$lib/db'
  import { invalidate } from '$app/navigation'
  import { onMount } from 'svelte'

  onMount(() => {
    const {
      data: { subscription },
    } = supabaseClient.auth.onAuthStateChange(() => {
      invalidate('supabase:auth')
    })

    return () => {
      subscription.unsubscribe()
    }
  })
</script>

<slot />
```

</TabPanel>
</Tabs>

#### Set up hooks [#migration-set-up-hooks-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-7"
  queryGroup="migration-version"
>
<TabPanel id="older-0-7" label="0.7.x">

```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'

export const handle = auth()
```

**Optional** _if using additional handle methods_

```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'
import { sequence } from '@sveltejs/kit/hooks'

export const handle = sequence(auth(), yourHandler)
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
```

```ts src/hooks.client.ts
// make sure the supabase instance is initialized on the client
import '$lib/db'
```

</TabPanel>
</Tabs>

#### Types [#migration-typings-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-7"
  queryGroup="migration-version"
>
<TabPanel id="older-0-7" label="0.7.x">

```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
  interface Locals {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }

  interface PageData {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }

  // interface Error {}
  // interface Platform {}
}
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
  interface Supabase {
    Database: import('./DatabaseDefinitions').Database
    SchemaName: 'public'
  }

  // interface Locals {}
  interface PageData {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }
  // interface Error {}
  // interface Platform {}
}
```

</TabPanel>
</Tabs>

#### `withPageAuth` [#migration-with-page-auth-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-7"
  queryGroup="migration-version"
>
<TabPanel id="older-0-7" label="0.7.x">

```svelte src/routes/protected-route/+page.svelte
<script lang="ts">
  import type { PageData } from './$types'

  export let data: PageData
  $: ({ tableData, user } = data)
</script>

<div>Protected content for {user.email}</div>
<p>server-side fetched data with RLS:</p>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts src/routes/protected-route/+page.ts
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
import type { PageLoad } from './$types'

export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) => {
  if (!session.user) {
    redirect(303, '/')
  }

  const { data: tableData } = await getSupabaseClient().from('test').select('*')
  return { tableData, user: session.user }
})
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```svelte src/routes/protected-route/+page.svelte
<script>
  /** @type {import('./$types').PageData} */
  export let data
  $: ({ user, tableData } = data)
</script>

<div>Protected content for {user.email}</div>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts src/routes/protected-route/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'

export const load: PageLoad = async (event) => {
  const { session, supabaseClient } = await getSupabase(event)
  if (!session) {
    redirect(303, '/')
  }
  const { data: tableData } = await supabaseClient.from('test').select('*')

  return {
    user: session.user,
    tableData,
  }
}
```

</TabPanel>
</Tabs>

#### `withApiAuth` [#migration-with-api-auth-0-8]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older-0-7"
  queryGroup="migration-version"
>
<TabPanel id="older-0-7" label="0.7.x">

```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'

interface TestTable {
  id: string
  created_at: string
}

export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient }) => {
  if (!session.user) {
    redirect(303, '/')
  }

  const { data } = await getSupabaseClient().from<TestTable>('test').select('*')

  return json({ data })
})
```

</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">

```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'

export const GET: RequestHandler = async (event) => {
  const { session, supabaseClient } = await getSupabase(event)
  if (!session) {
    redirect(303, '/')
  }
  const { data } = await supabaseClient.from('test').select('*')

  return json({ data })
}
```

</TabPanel>
</Tabs>

### Migrate from 0.6.11 and below to 0.7.0 [#migration-0-7]

There are numerous breaking changes in the latest 0.7.0 version of this library.

#### Environment variable prefix

The environment variable prefix is now `PUBLIC_` instead of `VITE_` (e.g., `VITE_SUPABASE_URL` is now `PUBLIC_SUPABASE_URL`).

#### Set up the Supabase client [#migration-set-up-supabase-client-0-7]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```js src/lib/db.ts
import { createSupabaseClient } from '@supabase/auth-helpers-sveltekit';

const { supabaseClient } = createSupabaseClient(
  import.meta.env.VITE_SUPABASE_URL as string,
  import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY as string
);

export { supabaseClient };
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```js src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
import { env } from '$env/dynamic/public'
// or use the static env

// import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public';

export const supabaseClient = createClient(
  env.PUBLIC_SUPABASE_URL,
  env.PUBLIC_SUPABASE_PUBLISHABLE_KEY,
  {
    persistSession: false,
    autoRefreshToken: false,
  }
)

setupSupabaseHelpers({
  supabaseClient,
  cookieOptions: {
    secure: !dev,
  },
})
```

</TabPanel>
</Tabs>

#### Initialize the client [#migration-initialize-client-0-7]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```svelte src/routes/__layout.svelte
<script>
  import { session } from '$app/stores'
  import { supabaseClient } from '$lib/db'
  import { SupaAuthHelper } from '@supabase/auth-helpers-svelte'
</script>

<SupaAuthHelper {supabaseClient} {session}>
  <slot />
</SupaAuthHelper>
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

The `@supabase/auth-helpers-svelte` library is no longer required as the `@supabase/auth-helpers-sveltekit` library handles all the client-side code.

```svelte src/routes/+layout.svelte
<script lang="ts">
  // make sure the supabase instance is initialized on the client
  import '$lib/db'
  import { startSupabaseSessionSync } from '@supabase/auth-helpers-sveltekit'
  import { page } from '$app/stores'
  import { invalidateAll } from '$app/navigation'

  // this sets up automatic token refreshing
  startSupabaseSessionSync({
    page,
    handleRefresh: () => invalidateAll(),
  })
</script>

<slot />
```

</TabPanel>
</Tabs>

#### Set up hooks [#migration-set-up-hooks-0-7]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```ts src/hooks.ts
import { handleAuth } from '@supabase/auth-helpers-sveltekit'
import type { GetSession, Handle } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'

export const handle: Handle = sequence(...handleAuth())

export const getSession: GetSession = async (event) => {
  const { user, accessToken, error } = event.locals
  return {
    user,
    accessToken,
    error,
  }
}
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'

export const handle = auth()
```

**Optional** _if using additional handle methods_

```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
import { auth } from '@supabase/auth-helpers-sveltekit/server'
import { sequence } from '@sveltejs/kit/hooks'

export const handle = sequence(auth(), yourHandler)
```

</TabPanel>
</Tabs>

#### Types [#migration-typings-0-7]

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare namespace App {
  interface UserSession {
    user: import('@supabase/supabase-js').User
    accessToken?: string
  }

  interface Locals extends UserSession {
    error: import('@supabase/supabase-js').ApiError
  }

  interface Session extends UserSession {}

  // interface Platform {}
  // interface Stuff {}
}
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />

// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
  interface Locals {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }

  interface PageData {
    session: import('@supabase/auth-helpers-sveltekit').SupabaseSession
  }

  // interface Error {}
  // interface Platform {}
}
```

</TabPanel>
</Tabs>

#### Check the user on the client

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```svelte src/routes/index.svelte
<script>
  import { session } from '$app/stores'
</script>

{#if !$session.user}
<h1>I am not logged in</h1>
{:else}
<h1>Welcome {$session.user.email}</h1>
<p>I am logged in!</p>
{/if}
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```svelte src/routes/+page.svelte
<script>
  import { page } from '$app/stores'
</script>

{#if !$page.data.session.user}
<h1>I am not logged in</h1>
{:else}
<h1>Welcome {$page.data.session.user.email}</h1>
<p>I am logged in!</p>
{/if}
```

</TabPanel>
</Tabs>

#### `withPageAuth`

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```svelte src/routes/protected-route.svelte
<script lang="ts" context="module">
  import { supabaseServerClient, withPageAuth } from '@supabase/auth-helpers-sveltekit'
  import type { Load } from './__types/protected-page'

  export const load: Load = async ({ session }) =>
    withPageAuth(
      {
        redirectTo: '/',
        user: session.user,
      },
      async () => {
        const { data } = await supabaseServerClient(session.accessToken).from('test').select('*')
        return { props: { data, user: session.user } }
      }
    )
</script>

<script>
  export let data
  export let user
</script>

<div>Protected content for {user.email}</div>
<p>server-side fetched data with RLS:</p>
<pre>{JSON.stringify(data, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```svelte src/routes/protected-route/+page.svelte
<script lang="ts">
  import type { PageData } from './$types'

  export let data: PageData
  $: ({ tableData, user } = data)
</script>

<div>Protected content for {user.email}</div>
<p>server-side fetched data with RLS:</p>
<pre>{JSON.stringify(tableData, null, 2)}</pre>
<p>user:</p>
<pre>{JSON.stringify(user, null, 2)}</pre>
```

```ts src/routes/protected-route/+page.ts
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
import type { PageLoad } from './$types'

export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) => {
  if (!session.user) {
    redirect(303, '/')
  }

  const { data: tableData } = await getSupabaseClient().from('test').select('*')
  return { tableData, user: session.user }
})
```

</TabPanel>
</Tabs>

#### `withApiAuth`

<Tabs
  scrollable
  size="small"
  type="underlined"
  defaultActiveId="older"
  queryGroup="migration-version"
>
<TabPanel id="older" label="0.6.11 and below">

```ts src/routes/api/protected-route.ts
import { supabaseServerClient, withApiAuth } from '@supabase/auth-helpers-sveltekit'
import type { RequestHandler } from './__types/protected-route'

interface TestTable {
  id: string
  created_at: string
}

interface GetOutput {
  data: TestTable[]
}

export const GET: RequestHandler<GetOutput> = async ({ locals, request }) =>
  withApiAuth({ user: locals.user }, async () => {
    // Run queries with RLS on the server
    const { data } = await supabaseServerClient(request).from('test').select('*')

    return {
      status: 200,
      body: { data },
    }
  })
```

</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">

```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types';
import { withAuth } from '@supabase/auth-helpers-sveltekit';
import { json, redirect } from '@sveltejs/kit';

interface TestTable {
  id: string;
  created_at: string;
}

export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient }) => {
  if (!session.user) {
    redirect(303, '/');
  }

  const { data } = await getSupabaseClient()
    .from<TestTable>('test')
    .select('*');

  return json({ data });
);
```

</TabPanel>
</Tabs>

## Additional links

- [Auth Helpers Source code](https://github.com/supabase/auth-helpers)
- [SvelteKit example](https://github.com/supabase/auth-helpers/tree/main/examples/sveltekit)

</AccordionItem>

</Accordion>
