This documentation is for a legacy version of Next.js and Supabase. For the latest version, please visit the Next.js and Supabase V2 documentation

Creating a Record | Next.js Supabase SaaS Kit

In this tutorial we show how you can create a record in Postgres with Supabase and Next.js

Creating a record with Supabase is easy. We can use the insert method to create a record in a table.

We combine the mutation with Next.js Server Actions so that we get data revalidation on-the-fly - pretty magical! 🧙‍♂️

The Makerkit convention is to store these in files called mutations.ts in the lib folder of your entity.

For example, here is a mutations file for a task entity.

import type { SupabaseClient } from '@supabase/supabase-js';
import type { Database } from '~/database.types';
type Task = {
id: string;
name: string;
organizationId: string;
description: string;
dueDate: Date;
done: boolean;
};
export function createTask(
client: SupabaseClient<Database>,
task: Omit<Task, 'id'>
) {
return client.from('tasks').insert({
name: task.name,
organization_id: task.organizationId,
description: task.description,
due_date: task.dueDate,
done: task.done,
});
}

Ideally, the Task interface is defined externally, but I am showing it here for simplicity and clarity.

Calling the mutation in a Server Action

We can now export a server action that uses this function to create a task.

'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { createTask } from '~/lib/tasks/mutations';
import type Task from '~/lib/tasks/types/task';
import { withSession } from '~/core/generic/actions-utils';
import getSupabaseServerActionClient from '~/core/supabase/action-client';
import { parseOrganizationIdCookie } from '~/lib/server/cookies/organization.cookie';
import requireSession from '~/lib/user/require-session';
type CreateTaskParams = {
task: Omit<Task, 'id'>;
csrfToken: string;
};
export const createTaskAction = withSession(
async (params: CreateTaskParams) => {
const client = getSupabaseServerActionClient();
const session = await requireSession(client);
const uid = await parseOrganizationIdCookie(session.user.id);
const path = `/dashboard/${uid}/tasks`;
await createTask(client, params.task);
// revalidate the tasks page
revalidatePath(path, 'page');
// redirect to the tasks page
redirect(path);
},
);

The withSession function is a helper that ensures that the user is logged in. If not, it will redirect to the login page. Use it for all server actions that require a user to be logged in.

Calling the mutation in a React component

Finally, it's a matter of calling the Next.js Server Action within the React component that handles the form submission.

Assuming you have a form element, you can do something like this:

import { createTaskAction } from '~/lib/tasks/actions';
import useCsrfToken from '~/core/hooks/use-csrf-token';
function TaskForm() {
const csrfToken = useCsrfToken();
return (
<form action={createTaskAction}>
<input type="text" name="task.name" />
<input type="text" name="task.description" />
<input type="date" name="task.dueDate" />
<input type="hidden" name="csrfToken" value={csrfToken} />
<button type="submit">Create Task</button>
</form>
);
}

Adding the CSRF token is required in all POST requests. You can use the useCsrfToken hook to get the token and passing it as a hidden input.

You can also call the function imperatively if you don't use a traditional form component.

import { useTransition } from "react";
import useCsrfToken from '~/core/hooks/use-csrf-token';
const [isPending, starTransition] = useTransition()
const onSubmit = (task: Task) => {
startTransition(async () => {
await createTaskAction({ task, csrfToken });
})
};