ra-supabase-core
TypeScript icon, indicating that this package has built-in type declarations

2.2.0 • Public • Published

ra-supabase-core

This package provide a dataProvider, an authProvider and hooks to integrate Supabase with react-admin.

Installation

yarn add ra-supabase-core
# or
npm install ra-supabase-core

Usage

// in supabase.js
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient('YOUR_SUPABASE_URL', 'YOUR_SUPABASE_ANON_KEY');

// in dataProvider.js
import { supabaseDataProvider } from 'ra-supabase-core';
import { supabaseClient } from './supabase';

export const dataProvider = supabaseDataProvider({
    instanceUrl: 'YOUR_SUPABASE_URL',
    apiKey: 'YOUR_SUPABASE_ANON_KEY',
    supabaseClient
});

// in authProvider.js
import { supabaseAuthProvider } from 'ra-supabase-core';
import { supabase } from './supabase';

export const authProvider = supabaseAuthProvider(supabase, {
    getIdentity: async user => {
        const { data, error } = await supabase
            .from('userProfiles')
            .select('id, first_name, last_name')
            .match({ email: user.email })
            .single();

        if (!data || error) {
            throw new Error();
        }

        return {
            id: data.id,
            fullName: `${data.first_name} ${data.last_name}`,
        };
    },
});

// in App.js
import { Admin, Resource, ListGuesser } from 'react-admin';
import { dataProvider } from './dataProvider';
import { authProvider } from './authProvider';

export const MyAdmin = () => (
    <Admin dataProvider={dataProvider} authProvider={authProvider}>
        <Resource name="posts" list={ListGuesser} />
        <Resource name="authors" list={ListGuesser} />
    </Admin>
);

Features

DataProvider

ra-supabase is built on ra-data-postgrest that leverages PostgREST. As such, you have access the following features:

Filters operators

When specifying the source prop of filter inputs, you can either set it to the field name for simple equality checks or add an operator suffix for more control. For instance, the gte (Greater Than or Equal) or the ilike (Case insensitive like) operators:

const postFilters = [
    <TextInput label="Title" source="title@ilike" alwaysOn />,
    <TextInput label="Views" source="views@gte" />,
];

export const PostList = () => (
    <List filters={postFilters}>
        ...
    </List>
);

See the PostgREST documentation for a list of supported operators.

RLS

As users authenticate through supabase, you can leverage Row Level Security. Users identity will be propagated through the dataProvider if you provided the public API (anon) key. Keep in mind that passing the service_role key will bypass Row Level Security. This is not recommended.

Customizing the dataProvider

supabaseDataProvider also accepts the same options as the ra-data-postgrest dataProvider (except apiUrl), like primaryKeys or schema.

// in dataProvider.js
import { supabaseDataProvider } from 'ra-supabase-core';
import { supabaseClient } from './supabase';

export const dataProvider = supabaseDataProvider({
    instanceUrl: 'YOUR_SUPABASE_URL',
    apiKey: 'YOUR_SUPABASE_ANON_KEY',
    supabaseClient,
    primaryKeys: new Map([
        ['some_table', ['custom_id']],
        ['another_table', ['first_column', 'second_column']],
    ]),
    schema: () => localStorage.getItem("schema") || "api",
});

See the `ra-data-postgrest`` documentation for more details.

Authentication

ra-supabase supports email/password and OAuth authentication.

Email & Password Authentication

To login users using their email and password, call the login method returned by the useLogin hook with the user credentials as an object:

import { useLogin } from 'react-admin';

const myLoginForm = () => {
    const login = useLogin();
    const redirectTo = window.location.toString();

    const handleSubmit = (event) => {
        login({
            email: event.target.email.value,
            password: event.target.password.value,
        }, redirectTo);
    }

    return (
        <form onSubmit={handleSubmit}>
            <label htmlFor="email">Email</label>
            <input id="email" name="email" type="email" />
            <label htmlFor="password">Password</label>
            <input id="password" name="password" type="password" />
            <button type="submit">Login</button>
        </form>
    )
}

OAuth Authentication

To login users using the OAuth providers enabled on your Supabase instance, call the login method returned by the useLogin hook with an object containing the provider name:

import { useLogin } from 'react-admin';

const myLoginForm = () => {
    const login = useLogin();

    const loginWith = (provider) => {
        const redirectTo = window.location.toString();

        login({ provider, options: { redirectTo }).catch(
            error => {
                // The authProvide always reject for OAuth login but there will be no error
                // if the call actually succeeds. This is to avoid react-admin redirecting
                // immediately to the provided redirect prop before users are redirected to
                // the OAuth provider.
                if (error) {
                    notify((error as Error).message, { type: 'error' });
                }
            }
        );
    }

    return (
        <div>
            <button onClick={() => loginWith('github')}>Login with Github</button>
            <button onClick={() => loginWith('twitter')}>Login with Twitter</button>
        </div>
    )
}

Make sure you enabled the specified providers in your Supabase instance:

API

The supabaseDataProvider

The supabaseDataProvider leverages ra-data-postgrest. Please refer to their documentation to know how to use it.

The supabaseAuthProvider

The supabaseAuthProvider must be initialized with your supabase client and an optional function to call when we need to display the user identity. Here's an example that fetches the user identity from a userProfiles table:

// in authProvider.js
import { supabaseAuthProvider } from 'ra-supabase-core';
import { supabase } from './supabase';

export const authProvider = supabaseAuthProvider(supabase, {
    getIdentity: async user => {
        const { data, error } = await supabase
            .from('userProfiles')
            .select('id, first_name, last_name')
            .match({ email: user.email })
            .single();

        if (!data || error) {
            throw new Error();
        }

        return {
            id: data.id,
            fullName: `${data.first_name} ${data.last_name}`,
        };
    },
});

The supabaseAuthProvider supports an optional getpermissions method that allows you to fetch user permissions. Here's an example that fetches user permissions from a userPermissions table. You can then use the usePermissions hook to retrieve them later.

// in authProvider.js
import { supabaseAuthProvider } from 'ra-supabase-core';
import { supabase } from './supabase';

export const authProvider = supabaseAuthProvider(supabase, {
    getPermissions: async user => {
        const { data, error } = await supabase
            .from('userPermissions')
            .select('id, can_edit')
            .match({ email: user.email })
            .single();

        if (!data || error) {
            throw new Error();
        }

        return {
            id: data.id,
            canEdit: data.can_edit,
        };
    },
});

supabaseAuthProvider also provides an additional setPassword method. This method allows you to create UI for users to set their passwords after being invited for example. This could be done in a custom route. The method signature is the following:

setPassword({ access_token: string; password: string }): Promise<void>

The useSupabaseAccessToken hook

This hook returns the access token for the current user from the URL and redirects the user to the home page if no access token is found. The redirection url can be overridden or disabled. The name of the access token parameter to look for in the URL can also be overridden (access_token by default).

This is useful in pages related to authentication such as one which would allow invited users to set their password.

import { useSupabaseAccessToken } from 'ra-supabase-core';

const SetPasswordPage = () => {
    const access_token = useSupabaseAccessToken();

    // Logic and UI to set the user password
};

The useSetPassword hook

This hook returns a function you can call to set the current user password. The function requires the user access token. The hook accept an option object with the following optional properties:

  • onSuccess: A function called when the set password operation succeeds. By default, it redirects users to the home page.
  • onFailure: A function called when the set password operation fails. By default, it display an error notification.
import { useSupabaseAccessToken } from 'ra-supabase-core';

const SetPasswordPage = () => {
    const access_token = useSupabaseAccessToken();
    const setPassword = useSetPassword();

    const handleSubmit = event => {
        setPassword({
            access_token,
            password: event.currentTarget.elements.password.value,
        });
    };

    return (
        <form onSubmit={handleSubmit}>
            <label for="password">Choose a password:</label>
            <input id="password" name="password" type="password" />
            <button type="submit">Save</button>
        </form>
    );
};

The useRedirectIfAuthenticated hook

This hooks checks whether users are authenticated and redirect them to the provided route (which defaults to the home page) when they are.

This is useful inside a custom login page and is the behavior of react-admin default login page, extracted as a hook.

import { useRedirectIfAuthenticated } from 'react-admin';
const MyLoginPage = () => {
    useRedirectIfAuthenticated();

    // UI and logic for authentication
};

Roadmap

  • Add support for magic link authentication

Readme

Keywords

none

Package Sidebar

Install

npm i ra-supabase-core

Weekly Downloads

236

Version

2.2.0

License

MIT

Unpacked Size

166 kB

Total Files

73

Last publish

Collaborators

  • fzaninotto
  • djhi