Files
myeasycms-v2/docs/development/loading-data-from-database.mdoc
Giancarlo Buomprisco 7ebff31475 Next.js Supabase V3 (#463)
Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
2026-03-24 13:40:38 +08:00

231 lines
6.8 KiB
Plaintext

---
status: "published"
label: "Loading data from the DB"
order: 4
title: "Learn how to load data from the Supabase database"
description: "In this page we learn how to load data from the Supabase database and display it in our Next.js application."
---
Now that our database supports the data we need, we can start loading it into our application. We will use the `@makerkit/data-loader-supabase-nextjs` package to load data from the Supabase database.
Please check the [documentation](https://github.com/makerkit/makerkit/tree/main/packages/data-loader/supabase/nextjs) for the `@makerkit/data-loader-supabase-nextjs` package to learn more about how to use it.
This nifty package allows us to load data from the Supabase database and display it in our server components with support for pagination.
In the snippet below, we will:
1. Load the user's workspace data from the database. This allows us to get the user's account ID without further round-trips because the workspace is loaded by the user layout.
2. Load the user's tasks from the database.
3. Display the tasks in a table.
4. Use a search input to filter the tasks by title.
Let's take a look at the code:
```tsx
import { use } from 'react';
import { ServerDataLoader } from '@makerkit/data-loader-supabase-nextjs';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { Button } from '@kit/ui/button';
import { Heading } from '@kit/ui/heading';
import { If } from '@kit/ui/if';
import { Input } from '@kit/ui/input';
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { getTranslations } from 'next-intl/server';
import { TasksTable } from './_components/tasks-table';
import { UserAccountHeader } from './_components/user-account-header';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';
interface SearchParams {
page?: string;
query?: string;
}
export const generateMetadata = async () => {
const t = await getTranslations('account');
const title = t('homePage');
return {
title,
};
};
async function UserHomePage(props: { searchParams: Promise<SearchParams> }) {
const client = getSupabaseServerClient();
const { user } = use(loadUserWorkspace());
const searchParams = await props.searchParams;
const page = parseInt(searchParams.page ?? '1', 10);
const query = searchParams.query ?? '';
return (
<>
<UserAccountHeader
title={<Trans i18nKey={'common.homeTabLabel'} />}
description={<Trans i18nKey={'common.homeTabDescription'} />}
/>
<PageBody className={'space-y-4'}>
<div className={'flex items-center justify-between'}>
<div>
<Heading level={4}>
<Trans i18nKey={'tasks.tasksTabLabel'} defaults={'Tasks'} />
</Heading>
</div>
<div className={'flex items-center space-x-2'}>
<form className={'w-full'}>
<Input
name={'query'}
defaultValue={query}
className={'w-full lg:w-[18rem]'}
placeholder={'Search tasks'}
/>
</form>
</div>
</div>
<ServerDataLoader
client={client}
table={'tasks'}
page={page}
where={{
account_id: {
eq: user.id,
},
title: {
textSearch: query ? `%${query}%` : undefined,
},
}}
>
{(props) => {
return (
<div className={'flex flex-col space-y-8'}>
<If condition={props.count === 0 && query}>
<div className={'flex flex-col space-y-2.5'}>
<p>
<Trans
i18nKey={'tasks.noTasksFound'}
values={{ query }}
/>
</p>
<form>
<input type="hidden" name={'query'} value={''} />
<Button variant={'outline'} size={'sm'}>
<Trans i18nKey={'tasks.clearSearch'} />
</Button>
</form>
</div>
</If>
<TasksTable {...props} />
</div>
);
}}
</ServerDataLoader>
</PageBody>
</>
);
}
export default UserHomePage;
```
Let's break this down a bit:
1. We import the necessary components and functions.
2. We define the `SearchParams` interface to type the search parameters.
3. We define the `generateMetadata` function to generate the page metadata.
4. We define the `UserHomePage` component that loads the user's workspace and tasks from the database.
5. We define the `ServerDataLoader` component that loads the tasks from the database.
6. We render the tasks in a table and provide a search input to filter the tasks by title.
7. We export the `UserHomePage` component.
### Displaying the tasks in a table
Now, let's show the tasks table component:
```tsx
'use client';
import Link from 'next/link';
import { ColumnDef } from '@tanstack/react-table';
import { Pencil } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { Button } from '@kit/ui/button';
import { DataTable } from '@kit/ui/enhanced-data-table';
import { Database } from '~/lib/database.types';
type Task = Database['public']['Tables']['tasks']['Row'];
export function TasksTable(props: {
data: Task[];
page: number;
pageSize: number;
pageCount: number;
}) {
const columns = useGetColumns();
return (
<div>
<DataTable {...props} columns={columns} />
</div>
);
}
function useGetColumns(): ColumnDef<Task>[] {
const t = useTranslations('tasks');
return [
{
header: t('task'),
cell: ({ row }) => (
<Link
className={'hover:underline'}
href={`/home/tasks/${row.original.id}`}
>
{row.original.title}
</Link>
),
},
{
header: t('createdAt'),
accessorKey: 'created_at',
},
{
header: t('updatedAt'),
accessorKey: 'updated_at',
},
{
header: '',
id: 'actions',
cell: ({ row }) => {
const id = row.original.id;
return (
<div className={'flex justify-end space-x-2'}>
<Link href={`/home/tasks/${id}`}>
<Button variant={'ghost'} size={'icon'}>
<Pencil className={'h-4'} />
</Button>
</Link>
</div>
);
},
},
];
}
```
In this snippet, we define the `TasksTable` component that renders the tasks in a table. We use the `DataTable` component from the `@kit/ui/enhanced-data-table` package to render the table.
We also define the `useGetColumns` hook that returns the columns for the table. We use the `useTranslations` hook from `next-intl` to translate the column headers.