## Installation ### Install as package You can install `@supabase/supabase-js` via the terminal. ```sh npm install @supabase/supabase-js ``` ```sh yarn add @supabase/supabase-js ``` ```sh pnpm add @supabase/supabase-js ``` ### Install via CDN You can install `@supabase/supabase-js` via CDN links. ```html ``` ```html ``` ### Use at runtime in Deno You can use `supabase-js` in the Deno runtime via [JSR](https://jsr.io/@supabase/supabase-js): ```ts import { createClient } from 'npm:@supabase/supabase-js@2' ``` --- ## Initializing Create a new client for use in the browser.

Examples

```ts import { createClient } from '@supabase/supabase-js' // Create a single supabase client for interacting with your database const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key') ```
API > Exposed schemas](/dashboard/project/_/settings/api) and add the schema which you want to expose to the API.\n\nNote: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.' } />
```ts import { createClient } from '@supabase/supabase-js' // Use a custom domain as the supabase URL const supabase = createClient('https://my-custom-domain.com', 'publishable-or-anon-key') ```
```ts import { createClient } from '@supabase/supabase-js' const options = { db: { schema: 'public', }, auth: { autoRefreshToken: true, persistSession: true, detectSessionInUrl: true, }, global: { headers: { 'x-my-custom-header': 'my-app-name' }, }, } const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', options) ```
```ts import { createClient } from '@supabase/supabase-js' const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', { // Provide a custom schema. Defaults to "public". db: { schema: 'other_schema' }, }) ```
API > Exposed schemas](/dashboard/project/_/settings/api) and add the schema which you want to expose to the API.\n\nNote: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema.' } />
```ts import { createClient } from '@supabase/supabase-js' const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', { global: { fetch: fetch.bind(globalThis) }, }) ```
```ts import 'react-native-url-polyfill/auto' import { createClient } from '@supabase/supabase-js' import AsyncStorage from '@react-native-async-storage/async-storage' const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', { auth: { storage: AsyncStorage, autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, }, }) ```
```ts import 'react-native-url-polyfill/auto' import { createClient } from '@supabase/supabase-js' import AsyncStorage from '@react-native-async-storage/async-storage' import * as SecureStore from 'expo-secure-store' import * as aesjs from 'aes-js' import 'react-native-get-random-values' // As Expo's SecureStore does not support values larger than 2048 // bytes, an AES-256 key is generated and stored in SecureStore, while // it is used to encrypt/decrypt values stored in AsyncStorage. class LargeSecureStore { private async _encrypt(key: string, value: string) { const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8)) const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1)) const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value)) await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey)) return aesjs.utils.hex.fromBytes(encryptedBytes) } private async _decrypt(key: string, value: string) { const encryptionKeyHex = await SecureStore.getItemAsync(key) if (!encryptionKeyHex) { return encryptionKeyHex } const cipher = new aesjs.ModeOfOperation.ctr( aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1) ) const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value)) return aesjs.utils.utf8.fromBytes(decryptedBytes) } async getItem(key: string) { const encrypted = await AsyncStorage.getItem(key) if (!encrypted) { return encrypted } return await this._decrypt(key, encrypted) } async removeItem(key: string) { await AsyncStorage.removeItem(key) await SecureStore.deleteItemAsync(key) } async setItem(key: string, value: string) { const encrypted = await this._encrypt(key, value) await AsyncStorage.setItem(key, encrypted) } } const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-anon-key', { auth: { storage: new LargeSecureStore(), autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, }, }) ```
```ts import { createClient } from '@supabase/supabase-js' const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') const { data } = await supabase.from('profiles').select('*') ```
--- ## 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](https://www.postgresql.org/docs/current/ddl-generated-columns.html). 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](/docs/reference/cli/supabase-gen-types). You can also generate the types [from the dashboard](https://supabase.com/dashboard/project/_/api?page=tables-intro). ```bash Terminal 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: ```sql create table public.movies ( id bigint generated always as identity primary key, name text not null, data jsonb null ); ``` ```ts ./database.types.ts 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: ```ts ./index.tsx import { createClient } from '@supabase/supabase-js' import { Database } from './database.types' const supabase = createClient(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](https://github.com/sindresorhus/type-fest), you can override the types like so: ```ts ./database-generated.types.ts export type Json = // ... export interface Database { // ... } ``` ```ts ./database.types.ts 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: ```ts // Partial type override allows you to only override some of the properties in your results const { data } = await supabase.from('countries').select().overrideTypes>() // For a full replacement of the original return type use the `{ merge: false }` property as second argument const { data } = await supabase .from('countries') .select() .overrideTypes, { merge: false }>() // Use it with `maybeSingle` or `single` const { data } = await supabase.from('countries').select().single().overrideTypes<{ id: string }>() ``` The generated types provide shorthands for accessing tables and enums. ```ts ./index.ts 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: ```sql create table countries ( "id" serial primary key, "name" text ); create table cities ( "id" serial primary key, "name" text, "country_id" int references "countries" ); ``` ```ts import { QueryResult, QueryData, QueryError } from '@supabase/supabase-js' const countriesWithCitiesQuery = supabase.from('countries').select(` id, name, cities ( id, name ) `) type CountriesWithCities = QueryData const { data, error } = await countriesWithCitiesQuery if (error) throw error const countriesWithCities: CountriesWithCities = data ``` ---