Writing data to Database

Learn how to write, update and delete data using the Supabase Database in Makerkit

Similarly to reading data, we may want to create a custom hook also when we write, update or delete data to our Supabase database.

Assuming we have an entity called tasks, and we want to create a hook to create a new Task, we can do the following.

  1. First, we create a new hook at lib/tasks/hooks/use-create-task.ts
  2. We add the tasks table name to lib/db-tables.ts
  3. We create a mutations.ts file within lib/tasks
export const TASKS_TABLE = `tasks`;

In our mutations file, we will add all the mutations we want to perform on the tasks table. In this case, we will add a createTask mutation.

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

Now, we can use the createTask mutation in our useCreateTask hook.

We will be using the useMutation hook from react-query to create our hook.

import useSWRMutation from 'swr/mutation';
import { useRouter } from 'next/navigation';
import useSupabase from '~/core/hooks/use-supabase';
import { createTask } from '~/lib/tasks/mutations';
import type Task from '~/lib/tasks/types/task';
function useCreateTaskMutation() {
  const client = useSupabase();
  const router = useRouter();
  const key = 'tasks';
  return useSWRMutation(key, async (_, { arg: task }: { arg: Omit<Task, 'id'> }) => {
    return createTask(client, task);
  }, {
    onSuccess: () => router.refresh()
export default useCreateTaskMutation;

Let's take a look at a complete example of a form that makes a request using the hook above:

import { useRouter } from 'next/router';
import type { FormEventHandler } from 'react';
import { useCallback } from 'react';
import toaster from 'react-hot-toast';
import TextField from '~/core/ui/TextField';
import Button from '~/core/ui/Button';
import useCreateTaskMutation from '~/lib/tasks/hooks/use-create-task';
import useCurrentOrganization from '~/lib/organizations/hooks/use-current-organization';
const CreateTaskForm = () => {
  const createTaskMutation = useCreateTaskMutation();
  const router = useRouter();
  const organization = useCurrentOrganization();
  const organizationId = organization?.id as number;
  const onCreateTask: FormEventHandler<HTMLFormElement> = useCallback(
    async (event) => {
      const target = event.currentTarget;
      const data = new FormData(target);
      const name = data.get('name') as string;
      const dueDate = (data.get('dueDate') as string) || getDefaultDueDate();
      if (name.trim().length < 3) {
        toaster.error('Task name must be at least 3 characters long');
      const task = {
        done: false,
      // create task
      await createTaskMutation.trigger(task);
      // redirect to /tasks
      return router.push(`/tasks`);
    [router, createTaskMutation, organizationId]
  return (
    <form onSubmit={onCreateTask}>
            placeholder={'ex. Launch on IndieHackers'}
          <TextField.Hint>Hint: whatever you do, ship!</TextField.Hint>
          Due date
          <TextField.Input name={'dueDate'} type={'date'} />
          <Button>Create Task</Button>
export default CreateTaskForm;
function getDefaultDueDate() {
  const date = new Date();
  date.setDate(date.getDate() + 1);
  date.setHours(23, 59, 59);
  return date.toDateString();

Subscribe to our Newsletter
Get the latest updates about React, Remix, Next.js, Firebase, Supabase and Tailwind CSS