> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hexclave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Supabase

> Integrate Hexclave with Supabase RLS

This guide shows how to integrate Hexclave with Supabase row level security (RLS).

<Info>
  This guide only focuses on the RLS/JWT integration and does not sync user data between Supabase and Stack. You should use [webhooks](/guides/apps/webhooks/overview) to achieve data sync.
</Info>

## Setup

Let's create a sample table and some RLS policies to demonstrate how to integrate Hexclave with Supabase RLS. You can apply the same logic to your own tables and policies.

<Steps>
  <Step title="Setup Supabase">
    First, let's create a Supabase project, then go to the [SQL Editor](https://supabase.com/dashboard/project/_/sql/new) and create a new table with some sample data and RLS policies.

    ```sql title="Supabase SQL Editor" theme={null}
    -- Create the 'data' table
    CREATE TABLE data (
      id bigint PRIMARY KEY,
      text text NOT NULL,
      user_id UUID
    );

    -- Insert sample data
    INSERT INTO data (id, text, user_id) VALUES
      (1, 'Everyone can see this', NULL),
      (2, 'Only authenticated users can see this', NULL),
      (3, 'Only user with specific id can see this', NULL);

    -- Enable Row Level Security
    ALTER TABLE data ENABLE ROW LEVEL SECURITY;

    -- Allow everyone to read the first row
    CREATE POLICY "Public read" ON "public"."data" TO public
    USING (id = 1);

    -- Allow authenticated users to read the second row
    CREATE POLICY "Authenticated access" ON "public"."data" TO authenticated
    USING (id = 2);

    -- Allow only the owner of the row to read it
    CREATE POLICY "User access" ON "public"."data" TO authenticated
    USING (id = 3 AND auth.uid() = user_id);
    ```
  </Step>

  <Step title="Setup a new Next.js project">
    Now let's create a new Next.js project and install Hexclave and Supabase client. (more details on [Next.js setup](https://nextjs.org/docs/getting-started/installation), [Hexclave setup](/guides/getting-started/setup), and [Supabase setup](https://supabase.com/docs/guides/getting-started/quickstarts/nextjs))

    ```bash title="Terminal" theme={null}
    npx create-next-app@latest -e with-supabase stack-supabase
    cd stack-supabase
    ```

    Then follow the [Hexclave setup prompt](/guides/getting-started/setup). Choose **Next.js** and **Supabase** in the setup builder, then copy the generated prompt into your coding agent.

    Now copy the environment variables from the Supabase dashboard to the `.env.local` file:

    * `NEXT_PUBLIC_SUPABASE_URL`
    * `NEXT_PUBLIC_SUPABASE_ANON_KEY`
    * `SUPABASE_JWT_SECRET`

    Copy environment variables from the Hexclave dashboard to the `.env.local` file.

    * `NEXT_PUBLIC_HEXCLAVE_PROJECT_ID`
    * `NEXT_PUBLIC_HEXCLAVE_PUBLISHABLE_CLIENT_KEY`
    * `HEXCLAVE_SECRET_SERVER_KEY`
  </Step>

  <Step title="Set up Supabase client">
    Now let's create a server action that mints a supabase JWT with the Hexclave user ID if the user is authenticated.

    ```tsx title="/utils/actions.ts" theme={null}
    'use server';

    import { hexclaveServerApp } from "@/hexclave/server";
    import * as jose from "jose";

    export const getSupabaseJwt = async () => {
      const user = await hexclaveServerApp.getUser();

      if (!user) {
        return null;
      }

      const token = await new jose.SignJWT({
        sub: user.id,
        role: "authenticated",
      })
        .setProtectedHeader({ alg: "HS256" })
        .setIssuedAt()
        .setExpirationTime('1h')
        .sign(new TextEncoder().encode(process.env.SUPABASE_JWT_SECRET));

      return token;
    };
    ```

    And now create a helper function to create a Supabase client with the JWT signed by the server action

    ```tsx title="/utils/supabase-client.ts" theme={null}
    import { createBrowserClient } from "@supabase/ssr";
    import { getSupabaseJwt } from "./actions";

    export const createSupabaseClient = () => {
      return createBrowserClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        { accessToken: async () => await getSupabaseJwt() || "" }
      );
    }
    ```
  </Step>

  <Step title="Fetch data from Supabase">
    Let's create an example page that fetches data from Supabase and displays it.

    ```tsx title="/app/page.tsx" theme={null}
    'use client';

    import { createSupabaseClient } from "@/utils/supabase-client";
    import { useHexclaveApp, useUser } from "@hexclave/next";
    import { useEffect, useState } from "react";

    export default function Page() {
      const app = useHexclaveApp();
      const user = useUser();
      const supabase = createSupabaseClient();
      const [data, setData] = useState<null | any[]>(null);

      useEffect(() => {
        supabase.from("data").select().then(({ data }) => setData(data ?? []));
      }, []);

      const listContent = data === null ?
        <p>Loading...</p> :
        data.length === 0 ?
          <p>No notes found</p> :
          data.map((note) => <li key={note.id}>{note.text}</li>);

      return (
        <div>
          {
            user ?
            <>
              <p>You are signed in</p>
              <p>User ID: {user.id}</p>
                <button onClick={async () => await app.redirectToSignOut()}>Sign Out</button>
            </> :
              <button onClick={async () => await app.redirectToSignIn()}>Sign In</button>
          }
          <h3>Supabase data</h3>
          <ul>{listContent}</ul>
        </div>
      )
    }
    ```

    Now you should be able to compare the data you can view with an anonymous user, an authenticated user. You can also add your user Id to the row 3 of the Supabase table, and you should be able to see the row if and only if you are signed in with that user.
  </Step>
</Steps>

You can find the full example [here on GitHub](https://github.com/hexclave/hexclave/tree/main/examples/supabase).
