When fetching data from the Supabase Postgres Database, we use the Supabase JS client.
To make data-fetching from Supabase more reusable, our convention is to create a custom React hook for each query we make.
For example, let's take a look at the hook to fetch an organization from Supabase.
First, we want to write a query that receives two parameters:
- a Supabase client interface
- an organization ID
export function getOrganizationById(
client: Client,
organizationId: number
) {
return client
.from('organizations')
.select(`
id,
name,
logoURL: logo_url
`)
.eq('id', organizationId)
.throwOnError()
.single();
}
It's important that we pass the client as a parameter so that we can use the function in both the browser and the server.
Now, if we want to use a React Hook to fetch the organization from one of
our components, we can use the useSWR
hook from swr
:
import useSupabase from '~/core/hooks/use-supabase';
import { getOrganizationById } from '~/lib/organizations/database/queries';
import useSWR from 'swr';
function useOrganizationQuery(
organizationId: number
) {
const client = useSupabase();
const key = ['organization', organizationId];
return useSWR(key, async () => {
return getOrganizationById(client, organizationId).then(
(result) => result.data
);
});
}
export default useOrganizationQuery;
There are two ways to use this function:
- Server: directly, in a server-side function such as: a Route Handler, a Server Action, a Server Component, or a Middleware
- Client: indirectly, in a React Hook in a client-side component
Retrieving data in a server-side function
Let's see some scenarios where we can use this function in a server-side.
Route Handler
When you are fetching data from a Route Handler, you can use the getSupabaseRouteHandlerClient
function.
import { getOrganizationById } from '~/lib/organizations/database/queries';
import getSupabaseRouteHandlerClient from '~/core/supabase/route-handler-client';
import { NextRequest, NextResponse } from "next/server";
export const GET = async (
req: NextRequest,
{ params }: { params: { id: string } }
) => {
const client = getSupabaseRouteHandlerClient();
const { data } = await getOrganizationById(client, params.id);
return NextResponse.json(data);
};
Server Action
When you are performing mutations using React Server Actions, you can use the getSupabaseServerActionClient
function to read data from the Supabase database.
'use server';
import getSupabaseServerActionClient from '~/core/supabase/action-client';
import { getOrganizationById } from '~/lib/organizations/database/queries';
export async function getOrganizationByIdAction(
organizationId: number
) {
const client = getSupabaseServerActionClient();
const { data } = await getOrganizationById(client, organizationId);
return data;
}
Server Component
When you are fetching data from a Server Component, you can use the getSupabaseServerComponentClient
function.
import getSupabaseServerComponentClient from '~/core/supabase/server-component-client';
import { getOrganizationById } from '~/lib/organizations/database/queries';
interface Params {
params: {
id: string;
};
}
async function OrganizationPage({ params }: Params) {
const client = getSupabaseServerComponentClient();
const organization = await getOrganizationById(client, params.id);
return <div>{organization.name}</div>;
}
Retrieving data using a React hook
Now that we have a hook to fetch an organization, we can use it in our components to retrieve the data.
import { useOrganizationQuery } from './use-organization-query';
function OrganizationCard({ organizationId }) {
const {
data: organization,
isLoading,
error
} = useOrganizationQuery(organizationId);
/* data is loading */
if (isLoading) {
return <div>Loading...</div>
}
/* request errored */
if (error) {
return <div>Error!</div>
}
/* request successful, we can access "organization" */
return <div>{organization.name}</div>;
}