Javascript Reference v1.0

JavaScript Client Library

@supabase/supabase-jsView on GitHub

This reference documents every object and method available in Supabase's isomorphic JavaScript library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.


Initializing

Create a new client for use in the browser.

Parameters

  • supabaseUrlRequiredstring

    The unique Supabase URL which is supplied when you create a new project in your project dashboard.

  • supabaseKeyRequiredstring

    The unique Supabase Key which is supplied when you create a new project in your project dashboard.

  • optionsOptionalSupabaseClientOptions
1
2
3
4
import { } from '@supabase/supabase-js' // Create a single supabase client for interacting with your database const = ('https://xyzcompany.supabase.co', 'public-anon-key')

Upgrade guide

supabase-js v2 focuses on "quality-of-life" improvements for developers and addresses some of the largest pain points in v1. v2 includes type support, a rebuilt Auth library with async methods, improved errors, and more.

No new features will be added to supabase-js v1 , but we'll continuing merging security fixes to v1, with maintenance patches for the next 3 months.

Upgrade the client library

Install the latest version

1
npm install @supabase/supabase-js@2

Optionally if you are using custom configuration with createClient then follow below:

1
2
3
4
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, { schema: 'custom', persistSession: false,})

Read more about the constructor options.

Auth methods

The signIn() method has been deprecated in favor of more explicit method signatures to help with type hinting. Previously it was difficult for developers to know what they were missing (e.g., a lot of developers didn't realize they could use passwordless magic links).

Sign in with email and password

1
2
3
const { user, error } = await supabase .auth .signIn({ email, password })
1
2
3
const { error } = await supabase .auth .signIn({ email })

Sign in with a third-party provider

1
2
3
const { error } = await supabase .auth .signIn({ provider })

Sign in with phone

1
2
3
const { error } = await supabase .auth .signIn({ phone, password })

Sign in with phone using OTP

1
2
3
4
const { error } = await supabase .auth .api .sendMobileOTP(phone)

Reset password for email

1
2
3
4
const { data, error } = await supabase .auth .api .resetPasswordForEmail(email)

Get the user's current session

1
const session = supabase.auth.session()

Get the logged-in user

1
const user = supabase.auth.user()

Update user data for a logged-in user

1
2
3
const { user, error } = await supabase .auth .update({ attributes })

Use a custom access_token JWT with Supabase

1
const { user, error } = supabase.auth.setAuth(access_token)

The cookie-related methods like setAuthCookie and getUserByCookie have been removed.

For Next.js you can use the Auth Helpers to help you manage cookies. If you can't use the Auth Helpers, you can use server-side rendering.

Some the PR for additional background information.

Data methods

.insert() / .upsert() / .update() / .delete() don't return rows by default: PR.

Previously, these methods return inserted/updated/deleted rows by default (which caused some confusion), and you can opt to not return it by specifying returning: 'minimal'. Now the default behavior is to not return rows. To return inserted/updated/deleted rows, add a .select() call at the end.

Insert and return data

1
2
3
const { data, error } = await supabase .from('my_table') .insert({ new_data })

Update and return data

1
2
3
4
const { data, error } = await supabase .from('my_table') .update({ new_data }) .eq('id', id)

Upsert and return data

1
2
3
const { data, error } = await supabase .from('my_table') .upsert({ new_data })

Delete and return data

1
2
3
4
const { data, error } = await supabase .from('my_table') .delete() .eq('id', id)

Realtime methods

Subscribe

1
2
3
4
const userListener = supabase .from('users') .on('*', (payload) => handleAllEventsPayload(payload.new)) .subscribe()

Unsubscribe

1
userListener.unsubscribe()

Fetch data

  • By default, Supabase projects will return a maximum of 1,000 rows. This setting can be changed in Project API Settings. It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use range() queries to paginate through your data.
  • select() can be combined with Modifiers
  • select() can be combined with Filters
  • If using the Supabase hosted platform apikey is technically a reserved keyword, since the API gateway will pluck it out for authentication. It should be avoided as a column name.
1
2
3
const { data, error } = await supabase .from('cities') .select()

Insert data

  • By default, every time you run insert(), the client library will make a select to return the full record. This is convenient, but it can also cause problems if your Policies are not configured to allow the select operation. If you are using Row Level Security and you are encountering problems, try setting the returning param to minimal.
1
2
3
4
5
const { data, error } = await supabase .from('cities') .insert([ { name: 'The Shire', country_id: 554 } ])

Update data

  • update() should always be combined with Filters to target the item(s) you wish to update.
1
2
3
4
const { data, error } = await supabase .from('characters') .update({ name: 'Han Solo' }) .match({ name: 'Han' })

Upsert data

  • Primary keys should be included in the data payload in order for an update to work correctly.
  • Primary keys must be natural, not surrogate. There are however, workarounds for surrogate primary keys.
  • If you need to insert new data and update existing data at the same time, use Postgres triggers.
1
2
3
const { data, error } = await supabase .from('messages') .upsert({ id: 3, message: 'foo', username: 'supabot' })

Delete data

  • delete() should always be combined with filters to target the item(s) you wish to delete.
  • If you use delete() with filters and you have RLS enabled, only rows visible through SELECT policies are deleted. Note that by default no rows are visible, so you need at least one SELECT/ALL policy that makes the rows visible.
1
2
3
4
const { data, error } = await supabase .from('cities') .delete() .match({ id: 666 })

Call a Postgres function

You can call Postgres functions as Remote Procedure Calls, logic in your database that you can execute from anywhere. Functions are useful when the logic rarely changes—like for password resets and updates.

1
2
3
create or replace function hello_world() returns text as $$ select 'Hello world';$$ language sql;
1
2
const { data, error } = await supabase .rpc('hello_world')

Using filters

Filters can be used on select(), update(), and delete() queries.

If a Postgres function returns a table response, you can also apply filters.

Applying Filters

You must apply your filters to the end of your query. For example:

1
2
3
4
5
6
7
8
9
const { data, error } = await supabase .from('cities') .select('name, country_id') .eq('name', 'The Shire') // Correctconst { data, error } = await supabase .from('cities') .eq('name', 'The Shire') // Incorrect .select('name, country_id')

Chaining

Filters can be chained together to produce advanced queries. For example:

1
2
3
4
5
const { data, error } = await supabase .from('cities') .select('name, country_id') .gte('population', 1000) .lt('population', 10000)

Conditional Chaining

Filters can be built up one step at a time and then executed. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
const filterByName = nullconst filterPopLow = 1000const filterPopHigh = 10000let query = supabase .from('cities') .select('name, country_id')if (filterByName) { query = query.eq('name', filterByName) }if (filterPopLow) { query = query.gte('population', filterPopLow) }if (filterPopHigh) { query = query.lt('population', filterPopHigh) }const { data, error } = await query

Column is equal to a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .eq('name', 'The shire')

Column is not equal to a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .neq('name', 'The shire')

Column is greater than a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .gt('country_id', 250)

Column is greater than or equal to a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .gte('country_id', 250)

Column is less than a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .lt('country_id', 250)

Column is less than or equal to a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .lte('country_id', 250)

Column matches a pattern

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .like('name', '%la%')

Column matches a case-insensitive pattern

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .ilike('name', '%la%')

Column is a value

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .is('name', null)

Column is in an array

1
2
3
4
const { data, error } = await supabase .from('characters') .select('name, book_id') .in('name', ['Harry', 'Hermione'])

Column contains every element in a value

  • .contains() can work on array columns or range columns. It is very useful for finding rows where a tag array contains all the values in the filter array.

    1
    2
    3
    4
    .contains('arraycol',["a","b"]) // You can use a javascript array for an array column.contains('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column..contains('rangecol','(1,2]') // Use Postgres range syntax for range column..contains('rangecol',`(${arr}]`) // You can insert an array into a string.
1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, main_exports') .contains('main_exports', ['oil'])

Contained by value

  • .containedBy() can work on array columns or range columns.

    1
    2
    3
    4
    .containedBy('arraycol',["a","b"]) // You can use a javascript array for an array column.containedBy('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column..containedBy('rangecol','(1,2]') // Use Postgres range syntax for range column..containedBy('rangecol',`(${arr}]`) // You can insert an array into a string.
1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, main_exports') .containedBy('main_exports', ['cars', 'food', 'machine'])

Greater than a range

1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, population_range_millions') .rangeGt('population_range_millions', '[150, 250]')

Greater than or equal to a range

1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, population_range_millions') .rangeGte('population_range_millions', '[150, 250]')

Less than or equal to a range

1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, population_range_millions') .rangeLt('population_range_millions', '[150, 250]')

Mutually exclusive to a range

1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, population_range_millions') .rangeAdjacent('population_range_millions', '[70, 185]')

With a common element

  • .overlaps() can work on array columns or range columns.

    1
    2
    3
    4
    .overlaps('arraycol',["a","b"]) // You can use a javascript array for an array column.overlaps('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column..overlaps('rangecol','(1,2]') // Use Postgres range syntax for range column..overlaps('rangecol',`(${arr}]`) // You can insert an array into a string.
1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, id, main_exports') .overlaps('main_exports', ['computers', 'minerals'])

Match a string


Match an associated value

1
2
3
4
const { data, error } = await supabase .from('characters') .select('name, book_id') .match({name: 'Harry', book_id: 156})

Don't match the filter

  • .not() expects you to use the raw PostgREST syntax for the filter names and values.

    1
    2
    3
    4
    5
    .not('name','eq','Luke') .not('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains. .not('rangecol','cs','(1,2]') // Use Postgres range syntax for range column. .not('id','in','(6,7)') // Use Postgres list () for in filter. .not('id','in',`(${arr})`) // You can insert a javascript array.
1
2
3
4
const { data, error } = await supabase .from('countries') .select('name, country_id') .not('name', 'eq', 'The Shire')

Match at least one filter

  • .or() expects you to use the raw PostgREST syntax for the filter names and values.

    1
    2
    3
    .or('id.in.(6,7), arraycol.cs.{"a","b"}') // Use Postgres list () for in filter. Array {} for array column and 'cs' for contains..or(`id.in.(${arrList}),arraycol.cs.{${arr}}`) // You can insert a javascipt array for list or array on array column..or(`id.in.(${arrList}),rangecol.cs.[${arrRange})`) // You can insert a javascipt array for list or range on a range column.
1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .or('id.eq.20,id.eq.30')

Match the filter

  • .filter() expects you to use the raw PostgREST syntax for the filter names and values, so it should only be used as an escape hatch in case other filters don't work.
    1
    2
    3
    4
    .filter('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains. .filter('rangecol','cs','(1,2]') // Use Postgres range syntax for range column. .filter('id','in','(6,7)') // Use Postgres list () for in filter. .filter('id','in',`(${arr})`) // You can insert a javascript array.
1
2
3
4
const { data, error } = await supabase .from('characters') .select('name, book_id') .filter('name', 'in', '("Harry","Hermione")')

Using modifiers

Modifiers can be used on select() queries.

If a Postgres function returns a table response, you can also apply modifiers to the rpc() function.


Order the results

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .order('id', { ascending: false })

Limit the number of rows returned

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .limit(1)

Limit the query to a range

1
2
3
4
const { data, error } = await supabase .from('cities') .select('name, country_id') .range(0,3)

Retrieve one row of data

1
2
3
4
5
const { data, error } = await supabase .from('cities') .select('name, country_id') .limit(1) .single()

Retrieve zero or one row of data

1
2
3
4
5
const { data, error } = await supabase .from('instruments') .select('name, section_id') .eq('name', 'violin') .maybeSingle()

Create a new user

  • By default, the user will need to verify their email address before logging in. If you would like to change this, you can disable "Email Confirmations" by going to Authentication -> Settings on supabase.com/dashboard
  • If "Email Confirmations" is turned on, a user is returned but session will be null
  • If "Email Confirmations" is turned off, both a user and a session will be returned
  • When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on supabase.com/dashboard
  • If signUp() is called for an existing confirmed user:
    • If "Enable email confirmations" is enabled on the "Authentication" -> "Settings" page, an obfuscated / fake user object will be returned.
    • If "Enable email confirmations" is disabled, an error with a message "User already registered" will be returned.
  • To check if a user already exists, refer to getUser().
1
2
3
4
const { user, session, error } = await supabase.auth.signUp({ email: 'example@email.com', password: 'example-password',})

Listen to auth events

1
2
3
supabase.auth.onAuthStateChange((event, session) => { console.log(event, session)})

Sign in a user

  • A user can sign up either via email or OAuth.
  • If you provide email without a password, the user will be sent a magic link.
  • The magic link's destination URL is determined by the SITE_URL config variable. To change this, you can go to Authentication -> Settings on supabase.com/dashboard
  • Specifying a provider will open the browser to the relevant login page.
1
2
3
4
const { user, session, error } = await supabase.auth.signIn({ email: 'example@email.com', password: 'example-password',})

Sign out a user

1
const { error } = await supabase.auth.signOut()

Send a password reset request

Sends a password reset request to an email address.

  • When the user clicks the reset link in the email they are redirected back to your application. You can configure the URL that the user is redirected to via the redirectTo param. See redirect URLs and wildcards to add additional redirect URLs to your project.
  • After the user has been redirected successfully, prompt them for a new password and call updateUser():
1
2
3
const { data, error } = await supabase.auth.update({ password: new_password,})
1
2
3
4
const { data, error } = await supabase.auth.api.resetPasswordForEmail( email, { redirectTo: 'https://example.com/update-password' })

Update a user

User email: By Default, email updates sends a confirmation link to both the user's current and new email. To only send a confirmation link to the user's new email, disable Secure email change in your project's email auth provider settings.

User metadata: It's generally better to store user data in a table within your public schema (i.e., public.users). Use the update() method if you have data which rarely changes or is specific only to the logged in user.

1
const { user, error } = await supabase.auth.update({email: 'new@email.com'})

Update the access token

1
2
3
4
5
6
7
8
9
10
11
function apiFunction(req, res) { // Assuming the access token was sent as a header "X-Supabase-Auth" const { access_token } = req.get('X-Supabase-Auth') // You can now use it within a Supabase Client const supabase = createClient("https://xyzcompany.supabase.co", "public-anon-key") const { user, error } = supabase.auth.setAuth(access_token) // This client will now send requests as this user const { data } = await supabase.from('your_table').select()}

Retrieve a user

This method gets the user object from memory.

1
const user = supabase.auth.user()

Retrieve a session

1
const session = supabase.auth.session()

Retrieve a user

  • Fetches the user object from the database instead of local storage.
  • Note that user() fetches the user object from local storage which might not be the most updated.
  • Requires the user's access_token.
1
2
3
const { user, error } = await supabase.auth.api.getUser( 'ACCESS_TOKEN_JWT',)

Invokes a Supabase Edge Function.

Invokes a function

Invokes a Supabase Edge Function.

  • Requires an Authorization header.
  • Invoke params generally match the Fetch API spec.

Parameters

  • functionNameRequiredstring

    The name of the Function to invoke.

  • optionsRequiredFunctionInvokeOptions

    Options for invoking the Function.

Return Type

Promise<One of the following options>
1
2
3
const { data, error } = await supabase.functions.invoke('hello', { body: JSON.stringify({ foo: 'bar' })})

Subscribe to channel

  • Realtime is disabled by default for new Projects for better database performance and security. You can turn it on by managing replication.
  • If you want to receive the "previous" data for updates and deletes, you will need to set REPLICA IDENTITY to FULL, like this: ALTER TABLE your_table REPLICA IDENTITY FULL;
1
2
3
4
5
6
const mySubscription = supabase .from('*') .on('*', payload => { console.log('Change received!', payload) }) .subscribe()

Remove a subscription

  • Removing subscriptions is a great way to maintain the performance of your project's database. Supabase will automatically handle cleanup 30 seconds after a user is disconnected, but unused subscriptions may cause degradation as more users are simultaneously subscribed.
1
supabase.removeSubscription(mySubscription)

Remove all subscriptions

  • Removing subscriptions is a great way to maintain the performance of your project's database. Supabase will automatically handle cleanup 30 seconds after a user is disconnected, but unused subscriptions may cause degradation as more users are simultaneously subscribed.
1
supabase.removeAllSubscriptions()

Retrieve subscriptions

1
const subscriptions = supabase.getSubscriptions()

Create a bucket

  • Policy permissions required:
    • buckets permissions: insert
    • objects permissions: none
1
2
3
const { data, error } = await supabase .storage .createBucket('avatars', { public: false })

Retrieve a bucket

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: none
1
2
3
const { data, error } = await supabase .storage .getBucket('avatars')

List all buckets

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: none
1
2
3
const { data, error } = await supabase .storage .listBuckets()

Update a bucket

  • Policy permissions required:
    • buckets permissions: update
    • objects permissions: none
1
2
3
const { data, error } = await supabase .storage .updateBucket('avatars', { public: false })

Delete a bucket

  • Policy permissions required:
    • buckets permissions: select and delete
    • objects permissions: none
1
2
3
const { data, error } = await supabase .storage .deleteBucket('avatars')

Empty a bucket

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: select and delete
1
2
3
const { data, error } = await supabase .storage .emptyBucket('avatars')

Upload a file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: insert
  • For React Native, using either Blob, File or FormData does not work as intended. Upload file using ArrayBuffer from base64 file data instead, see example below.
1
2
3
4
5
6
7
8
const avatarFile = event.target.files[0]const { data, error } = await supabase .storage .from('avatars') .upload('public/avatar1.png', avatarFile, { cacheControl: '3600', upsert: false })

Download a file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select
1
2
3
4
const { data, error } = await supabase .storage .from('avatars') .download('folder/avatar1.png')

List all files in a bucket

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select
1
2
3
4
5
6
7
8
const { data, error } = await supabase .storage .from('avatars') .list('folder', { limit: 100, offset: 0, sortBy: { column: 'name', order: 'asc' }, })

Replace an existing file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: update and select
  • For React Native, using either Blob, File or FormData does not work as intended. Update file using ArrayBuffer from base64 file data instead, see example below.
1
2
3
4
5
6
7
8
const avatarFile = event.target.files[0]const { data, error } = await supabase .storage .from('avatars') .update('public/avatar1.png', avatarFile, { cacheControl: '3600', upsert: false })

Move an existing file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: update and select
1
2
3
4
const { data, error } = await supabase .storage .from('avatars') .move('public/avatar1.png', 'private/avatar2.png')

Copy an existing file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: insert and select
1
2
3
4
const { data, error } = await supabase .storage .from('avatars') .copy('public/avatar1.png', 'private/avatar2.png')

Delete files in a bucket

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: delete and select
1
2
3
4
const { data, error } = await supabase .storage .from('avatars') .remove(['folder/avatar1.png'])

Create a signed URL

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select
1
2
3
4
const { signedURL, error } = await supabase .storage .from('avatars') .createSignedUrl('folder/avatar1.png', 60)

Create signed URLs

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select
1
2
3
4
const { data, error } = await supabase .storage .from('avatars') .createSignedUrls(['folder/avatar1.png', 'folder/avatar2.png'], 60)

Retrieve public URL

  • The bucket needs to be set to public, either via updateBucket() or by going to Storage on supabase.com/dashboard, clicking the overflow menu on a bucket and choosing "Make public"
  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: none
1
2
3
4
const { publicURL, error } = supabase .storage .from('public-bucket') .getPublicUrl('folder/avatar1.png')