REST API

Generating TypeScript Types

How to generate types for your API and Supabase libraries.


Supabase APIs are generated from your database, which means that we can use database introspection to generate type-safe API definitions.

Generating types from project dashboard

Supabase allows you to generate and download TypeScript types directly from the project dashboard.

Generating types using Supabase CLI

The Supabase CLI is a single binary Go application that provides everything you need to setup a local development environment.

You can install the CLI via npm or other supported package managers. The minimum required version of the CLI is v1.8.1.


_10
npm i supabase@">=1.8.1" --save-dev

Login with your Personal Access Token:


_10
npx supabase login

Before generating types, ensure you initialize your Supabase project:


_10
npx supabase init

Generate types for your project to produce the database.types.ts file:


_10
npx supabase gen types --lang=typescript --project-id "$PROJECT_REF" --schema public > database.types.ts

or in case of local development:


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

These types are generated from your database schema. Given a table public.movies, the generated types will look like:


_10
create table public.movies (
_10
id bigint generated always as identity primary key,
_10
name text not null,
_10
data jsonb null
_10
);

./database.types.ts

_28
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
_28
_28
export interface Database {
_28
public: {
_28
Tables: {
_28
movies: {
_28
Row: {
_28
// the data expected from .select()
_28
id: number
_28
name: string
_28
data: Json | null
_28
}
_28
Insert: {
_28
// the data to be passed to .insert()
_28
id?: never // generated columns must not be supplied
_28
name: string // `not null` columns with no default must be supplied
_28
data?: Json | null // nullable columns can be omitted
_28
}
_28
Update: {
_28
// the data to be passed to .update()
_28
id?: never
_28
name?: string // `not null` columns are optional on .update()
_28
data?: Json | null
_28
}
_28
}
_28
}
_28
}
_28
}

Using TypeScript type definitions

You can supply the type definitions to supabase-js like so:

./index.tsx

_10
import { createClient } from '@supabase/supabase-js'
_10
import { Database } from './database.types'
_10
_10
const supabase = createClient<Database>(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY)

Helper types for tables and joins

You can use the following helper types to make the generated TypeScript types easier to use.

Sometimes the generated types are not what you expect. For example, a view's column may show up as nullable when you expect it to be not null. Using type-fest, you can override the types like so:

./database-generated.types.ts

_10
export type Json = // ...
_10
_10
export interface Database {
_10
// ...
_10
}

./database.types.ts

_20
import { MergeDeep } from 'type-fest'
_20
import { Database as DatabaseGenerated } from './database-generated.types'
_20
export { Json } from './database-generated.types'
_20
_20
// Override the type for a specific column in a view:
_20
export type Database = MergeDeep<
_20
DatabaseGenerated,
_20
{
_20
public: {
_20
Views: {
_20
movies_view: {
_20
Row: {
_20
// id is a primary key in public.movies, so it must be `not null`
_20
id: number
_20
}
_20
}
_20
}
_20
}
_20
}
_20
>

You can also override the type of an individual successful response if needed:


_10
const { data } = await supabase.from('countries').select().returns<MyType>()

Type shorthands

The generated types provide shorthands for accessing tables and enums.

./index.ts

_10
import { Database, Tables, Enums } from "./database.types.ts";
_10
_10
// Before 😕
_10
let movie: Database['public']['Tables']['movies']['Row'] = // ...
_10
_10
// After 😍
_10
let movie: Tables<'movies'>

Response types for complex queries

supabase-js always returns a data object (for success), and an error object (for unsuccessful requests).

These helper types provide the result types from any query, including nested types for database joins.

Given the following schema with a relation between cities and countries:


_10
create table countries (
_10
"id" serial primary key,
_10
"name" text
_10
);
_10
_10
create table cities (
_10
"id" serial primary key,
_10
"name" text,
_10
"country_id" int references "countries"
_10
);

We can get the nested CountriesWithCities type like this:


_15
import { QueryResult, QueryData, QueryError } from '@supabase/supabase-js'
_15
_15
const countriesWithCitiesQuery = supabase.from('countries').select(`
_15
id,
_15
name,
_15
cities (
_15
id,
_15
name
_15
)
_15
`)
_15
type CountriesWithCities = QueryData<typeof countriesWithCitiesQuery>
_15
_15
const { data, error } = await countriesWithCitiesQuery
_15
if (error) throw error
_15
const countriesWithCities: CountriesWithCities = data

Update types automatically with GitHub Actions

One way to keep your type definitions in sync with your database is to set up a GitHub action that runs on a schedule.

Add the following script to your package.json to run it using npm run update-types


_10
"update-types": "npx supabase gen types --lang=typescript --project-id \"$PROJECT_REF\" > database.types.ts"

Create a file .github/workflows/update-types.yml with the following snippet to define the action along with the environment variables. This script will commit new type changes to your repo every night.


_41
name: Update database types
_41
_41
on:
_41
schedule:
_41
# sets the action to run daily. You can modify this to run the action more or less frequently
_41
- cron: '0 0 * * *'
_41
_41
jobs:
_41
update:
_41
runs-on: ubuntu-latest
_41
permissions:
_41
contents: write
_41
env:
_41
SUPABASE_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
_41
PROJECT_REF: <your-project-id>
_41
steps:
_41
- uses: actions/checkout@v2
_41
with:
_41
persist-credentials: false
_41
fetch-depth: 0
_41
- uses: actions/setup-node@v2.1.5
_41
with:
_41
node-version: 16
_41
- run: npm run update-types
_41
- name: check for file changes
_41
id: git_status
_41
run: |
_41
echo "status=$(git status -s)" >> $GITHUB_OUTPUT
_41
- name: Commit files
_41
if: ${{contains(steps.git_status.outputs.status, ' ')}}
_41
run: |
_41
git add database.types.ts
_41
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
_41
git config --local user.name "github-actions[bot]"
_41
git commit -m "Update database types" -a
_41
- name: Push changes
_41
if: ${{contains(steps.git_status.outputs.status, ' ')}}
_41
uses: ad-m/github-push-action@master
_41
with:
_41
github_token: ${{ secrets.GITHUB_TOKEN }}
_41
branch: ${{ github.ref }}

Alternatively, you can use a community-supported GitHub action: generate-supabase-db-types-github-action.

Resources