Prepare for the PgBouncer and IPv4 deprecations on 26th January 2024

Home

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.

Terminal

_10
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:


_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

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

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>(
_10
process.env.SUPABASE_URL,
_10
process.env.SUPABASE_ANON_KEY
_10
)

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>()

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


_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
);


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