Max Rohowsky, Ph.D.

53 views

Supabase Local Development

This post is summary of how to setup a local development environment for Supabase. There's a strong emphasis on the database because that's usually the tricky part. I like to use this post as a cheat sheet for my own reference.

Running Supabase locally

The Supabase command line interface (CLI) lets you to run the entire stack locally. Let's start by creating a new project:

npx supabase init

This creates a supabase folder with a .temp folder and the config.toml file. The former has unimportant stuff, the latter has important configuration stuff.

To start the Supabase stack, you must launch Docker Desktop and run the command below. This takes time the first time because the Docker images need to be downloaded.

npx supabase start

Congrats! You've just created a local development environment for Supabase.

Next, to check the status and get the local development URLs, run:

npx supabase status

To stop the stack, exchange <project_name> with the actual name of your project and run:

npx supabase stop --project-id <project_name>

Using Google auth locally

While testing, it helps to setup Auth to work locally. Here's a quick guide to setup Google Auth on a local Supabase instance. Setting up other Auth providers is similar.

Google Developer Console

Begin by creating new credentials for an OAuth 2.0 client ID in the APIs and services section of the Google Developer Console. Start the setup by clicking on the "Create credentials" button.

Google OAuth 2.0 client ID
Google OAuth 2.0 client ID

During the setup, add the following redirect URI:

http://localhost:54321/auth/v1/callback

That's the standard path for the callback. For testing purposes, the redirect points to your local Supabase instance (see Redirect URI). After walking though the setup, you will get a client ID and client secret (see Client ID and secret):

Redirect URI on Google Developer Console
Redirect URI on Google Developer Console

Supabase configuration

Back in the project, open the config.toml file and add the following to the auth_providers section:

[auth.external.google]
enabled = true
client_id = "env(GOOGLE_CLIENT_ID)"
secret = "env(GOOGLE_CLIENT_SECRET)"
redirect_uri = "http://127.0.0.1:54321/auth/v1/callback"
url = ""
skip_nonce_check = false

Now, create an .env file in the root of the project and add the id and secret you got from the Google Developer Console:

GOOGLE_CLIENT_ID="insert_id_here"
GOOGLE_CLIENT_SECRET="insert_secret_here"

For these changes to take effect, you need to stop and start Supabase. More details on local auth can be found in the Supabase documentation.

NextJS components

The Google auth flow works as follows:

  • User clicks login button which opens Google auth page
  • After successful login user is redirected to Supabase callback: localhost:54321/auth/v1/callback
  • Supabase processes OAuth and redirects to the callback in the NextJS app
  • App callback exchanges code for session and redirects to final destination

Here's an example of a Google login button component that uses the Supabase client to sign-in with Google.

// .../components/google-login.tsx

'use client';

import { FcGoogle } from "react-icons/fc";
import { Button } from "@/components/ui/button";
import { createClient } from "@/utils/supabase/client";
import { AppRoutes } from "@/routes";

export const GoogleLogin = () => {
  const supabase = createClient();

  const onClick = async () => {
    supabase.auth.signInWithOAuth({
      provider: "google",
      options: {
        redirectTo: `${origin}/api/auth/callback?redirect=${encodeURIComponent("/dashboard")}`,
      },
    });
  }

  return (
    <div className="flex items-center w-full gap-x-2">
      <Button
        size="lg"
        className="w-full"
        variant="outline"
        onClick={onClick}
      >
        <FcGoogle className="h-5 w-5" />
      </Button>
    </div>
  )
}

Local database migrations

Migrations let us version control changes — such as creating tables, adding columns, or inserting data — to a database. This ensures that database changes are consistently applied across different environments.

Create a new migration

Migrations are .sql files stored in the supabase/migrations folder. To create a new and empty migration file, run:

npx supabase migration new example_migration

This will create a new file with the name structure <timestamp>_example_migration.sql inside the supabase/migrations folder.

You can add any SQL statements to the migration file, such as:

-- Example migration file

create table if not exists example_table (
    id uuid default gen_random_uuid() primary key,
    message text not null
);

insert into example_table (message) values ('Hello, world!');

This migration file creates the example_table with an id and a message column and inserts a row with data into the table.

Apply a migration locally

By applying a migration, the SQL code in the migration files gets applied to the database on the local Supabase instance. To apply a migration such as the one above, run:

npx supabase migration up

Afterwards, you'll see the changes applied in the Supabase studio?Similar to the dashboard on the Supabase platform but tailored to local development. As you can see from the studio snapshot below, the table and row have been created from the content of the migration file:

Applying a migration
Applying a migration

Pull local database changes

Sometimes making changes via the studio (running locally) is more convenient then writing SQL code. To pull these changes back into our editor, we can write the new changes into a migration file using:

npx supabase db diff -f new_changes_migration

This creates a new migration file in supabase/migrations with the difference (aka. the diff) between the files stored in the migrations folder and the local database that we edited using the studio.

Here's an example of a migration file that contains the diff after adding a column to the example_table table in the Supabase studio:

-- Example 'diff'

alter table "public"."example_table" add column "user" text;

Dump the local database

A dump let's us get the schema and data from the database. Here's how to dump the local database schema, data and a specific schema:

npx supabase db dump --local -f supabase/dump_schema.sql

Reset the local database

A reset recreates the local Postgres container and applies all local migrations found in supabase/migrations directory

npx supabase db reset

If test data is defined in supabase/seed.sql, it will be seeded after the migrations are run.

Remote database migrations

Linking to a hosted project

Applying database migrations to the Supabase platform requires a link between your local project and the Supabase platform.

To create the link, insert your project_id (aka. project ref.) into:

npx supabase link --project-ref <project_id>

You'll be prompted for the database password which can be found in Settings / Database.

To remove the link, run:

npx supabase unlink

Apply a remote migration

Applying a migration to a linked remote database is done using:

supabase db push

Once pushed, you can check that the migration version is up to date for the local and remote database by running:

supabase migration list

Dump the remote database

The CLI commands are similar to the ones we used for the local database except that we don't use the --local flag:

npx supabase db dump -f supabase/dump_schema.sql

Reset the remote database

To reset the remote database using the migrations in your supabase/migrations folder, include the --linked flag:

npx supabase db reset --linked

Careful with this one. Make a data and schema backup before running this command.

Generating TypeScript Types

To generate TypeScript types for the local or remote database, run:

npx supabase gen types --local --lang=typescript > ./types.ts

More information on how to use the generated types file can be found in this YouTube video.