JavaScript: TypeScript support

supabase-js has TypeScript support for type inference, autocompletion, type-safe queries, and more.

With TypeScript, supabase-js detects things like not null constraints and generated columns. Nullable columns are typed as T | null when you select the column. Generated columns will show a type error when you insert to it.

supabase-js also detects relationships between tables. A referenced table with one-to-many relationship is typed as T[]. Likewise, a referenced table with many-to-one relationship is typed as T | null.

Generating TypeScript Types

You can use the Supabase CLI to generate the types. You can also generate the types from the dashboard.

supabase gen types typescript --project-id abcdefghijklmnopqrst > database.types.ts

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

create table public.movies (
  id bigint generated always as identity primary key,
  name text not null,
  data jsonb null
);
export type Json = string | number | boolean | null | \{ [key: string]: Json | undefined \} | Json[]

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

Using TypeScript type definitions

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

import \{ createClient \} from '@supabase/supabase-js'
import \{ Database \} from './database.types'

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:

export type Json = // ...

export interface Database \{
  // ...
\}
import \{ MergeDeep \} from 'type-fest'
import \{ Database as DatabaseGenerated \} from './database-generated.types'
export \{ Json \} from './database-generated.types'

// Override the type for a specific column in a view:
export type Database = MergeDeep<
  DatabaseGenerated,
  \{
    public: \{
      Views: \{
        movies_view: \{
          Row: \{
            // id is a primary key in public.movies, so it must be `not null`
            id: number
          \}
        \}
      \}
    \}
  \}
>

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

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

The generated types provide shorthands for accessing tables and enums.

import \{ Database, Tables, Enums \} from "./database.types.ts";

// Before 😕
let movie: Database['public']['Tables']['movies']['Row'] = // ...

// After 😍
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, we can get the nested CountriesWithCities type:

create table countries (
  "id" serial primary key,
  "name" text
);

create table cities (
  "id" serial primary key,
  "name" text,
  "country_id" int references "countries"
);
import \{ QueryResult, QueryData, QueryError \} from '@supabase/supabase-js'

const countriesWithCitiesQuery = supabase
  .from("countries")
  .select(`
    id,
    name,
    cities (
      id,
      name
    )
  `);
type CountriesWithCities = QueryData<typeof countriesWithCitiesQuery>;

const \{ data, error \} = await countriesWithCitiesQuery;
if (error) throw error;
const countriesWithCities: CountriesWithCities = data;