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

_10
import { createClient } from '@supabase/supabase-js'
_10
_10
// Create a single supabase client for interacting with your database
_10
const supabase = createClient('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


_10
npm install @supabase/supabase-js@2

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

src/supabaseClient.ts

_10
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
_10
schema: 'custom',
_10
persistSession: false,
_10
})

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


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


_10
const { error } = await supabase
_10
.auth
_10
.signIn({ email })

Sign in with a third-party provider


_10
const { error } = await supabase
_10
.auth
_10
.signIn({ provider })

Sign in with phone


_10
const { error } = await supabase
_10
.auth
_10
.signIn({ phone, password })

Sign in with phone using OTP


_10
const { error } = await supabase
_10
.auth
_10
.api
_10
.sendMobileOTP(phone)

Reset password for email


_10
const { data, error } = await supabase
_10
.auth
_10
.api
_10
.resetPasswordForEmail(email)

Get the user's current session


_10
const session = supabase.auth.session()

Get the logged-in user


_10
const user = supabase.auth.user()

Update user data for a logged-in user


_10
const { user, error } = await supabase
_10
.auth
_10
.update({ attributes })

Use a custom access_token JWT with Supabase


_10
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


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

Update and return data


_10
const { data, error } = await supabase
_10
.from('my_table')
_10
.update({ new_data })
_10
.eq('id', id)

Upsert and return data


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

Delete and return data


_10
const { data, error } = await supabase
_10
.from('my_table')
_10
.delete()
_10
.eq('id', id)

Realtime methods

Subscribe


_10
const userListener = supabase
_10
.from('users')
_10
.on('*', (payload) => handleAllEventsPayload(payload.new))
_10
.subscribe()

Unsubscribe


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

_10
const { data, error } = await supabase
_10
.from('cities')
_10
.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.

_10
const { data, error } = await supabase
_10
.from('cities')
_10
.insert([
_10
{ name: 'The Shire', country_id: 554 }
_10
])


Update data

  • update() should always be combined with Filters to target the item(s) you wish to update.

_10
const { data, error } = await supabase
_10
.from('cities')
_10
.update({ name: 'Middle Earth' })
_10
.match({ name: 'Auckland' })


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.

_10
const { data, error } = await supabase
_10
.from('messages')
_10
.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.

_10
const { data, error } = await supabase
_10
.from('cities')
_10
.delete()
_10
.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.


_10
create or replace function hello_world() returns text as $$
_10
select 'Hello world';
_10
$$ language sql;


_10
const { data, error } = await supabase
_10
.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:


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.eq('name', 'The Shire') // Correct
_10
_10
const { data, error } = await supabase
_10
.from('cities')
_10
.eq('name', 'The Shire') // Incorrect
_10
.select('name, country_id')

Chaining

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


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.gte('population', 1000)
_10
.lt('population', 10000)

Conditional Chaining

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


_13
const filterByName = null
_13
const filterPopLow = 1000
_13
const filterPopHigh = 10000
_13
_13
let query = supabase
_13
.from('cities')
_13
.select('name, country_id')
_13
_13
if (filterByName) { query = query.eq('name', filterByName) }
_13
if (filterPopLow) { query = query.gte('population', filterPopLow) }
_13
if (filterPopHigh) { query = query.lt('population', filterPopHigh) }
_13
_13
const { data, error } = await query


Column is equal to a value


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


Column is not equal to a value


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


Column is greater than a value


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


Column is greater than or equal to a value


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


Column is less than a value


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


Column is less than or equal to a value


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


Column matches a pattern


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


Column matches a case-insensitive pattern


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


Column is a value


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


Column is in an array


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.in('name', ['Rio de Janeiro', 'San Francisco'])


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.


    _10
    .contains('arraycol',["a","b"]) // You can use a javascript array for an array column
    _10
    .contains('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.
    _10
    .contains('rangecol','(1,2]') // Use Postgres range syntax for range column.
    _10
    .contains('rangecol',`(${arr}]`) // You can insert an array into a string.


_10
const { data, error } = await supabase
_10
.from('countries')
_10
.select('name, id, main_exports')
_10
.contains('main_exports', ['oil'])


Contained by value

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


    _10
    .containedBy('arraycol',["a","b"]) // You can use a javascript array for an array column
    _10
    .containedBy('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.
    _10
    .containedBy('rangecol','(1,2]') // Use Postgres range syntax for range column.
    _10
    .containedBy('rangecol',`(${arr}]`) // You can insert an array into a string.


_10
const { data, error } = await supabase
_10
.from('countries')
_10
.select('name, id, main_exports')
_10
.containedBy('main_exports', ['cars', 'food', 'machine'])


Greater than a range


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


Greater than or equal to a range


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


Less than or equal to a range


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


Mutually exclusive to a range


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


With a common element

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


    _10
    .overlaps('arraycol',["a","b"]) // You can use a javascript array for an array column
    _10
    .overlaps('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.
    _10
    .overlaps('rangecol','(1,2]') // Use Postgres range syntax for range column.
    _10
    .overlaps('rangecol',`(${arr}]`) // You can insert an array into a string.


_10
const { data, error } = await supabase
_10
.from('countries')
_10
.select('name, id, main_exports')
_10
.overlaps('main_exports', ['computers', 'minerals'])


Match a string


Match an associated value


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.match({name: 'Beijing', country_id: 156})


Don't match the filter

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


    _10
    .not('name','eq','Paris')
    _10
    .not('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains.
    _10
    .not('rangecol','cs','(1,2]') // Use Postgres range syntax for range column.
    _10
    .not('id','in','(6,7)') // Use Postgres list () for in filter.
    _10
    .not('id','in',`(${arr})`) // You can insert a javascript array.


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.not('name', 'eq', 'Paris')


Match at least one filter

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


    _10
    .or('id.in.(6,7), arraycol.cs.{"a","b"}') // Use Postgres list () for in filter. Array {} for array column and 'cs' for contains.
    _10
    .or(`id.in.(${arrList}),arraycol.cs.{${arr}}`) // You can insert a javascipt array for list or array on array column.
    _10
    .or(`id.in.(${arrList}),rangecol.cs.[${arrRange})`) // You can insert a javascipt array for list or range on a range column.


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.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.

    _10
    .filter('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains.
    _10
    .filter('rangecol','cs','(1,2]') // Use Postgres range syntax for range column.
    _10
    .filter('id','in','(6,7)') // Use Postgres list () for in filter.
    _10
    .filter('id','in',`(${arr})`) // You can insert a javascript array.


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.filter('name', 'in', '("Paris","Tokyo")')


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


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


Limit the number of rows returned


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


Limit the query to a range


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


Retrieve one row of data


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


Retrieve zero or one row of data


_10
const { data, error } = await supabase
_10
.from('cities')
_10
.select('name, country_id')
_10
.eq('name', 'Singapore')
_10
.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().

_10
const { user, session, error } = await supabase.auth.signUp({
_10
email: 'example@email.com',
_10
password: 'example-password',
_10
})


Listen to auth events


_10
supabase.auth.onAuthStateChange((event, session) => {
_10
console.log(event, session)
_10
})


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.

_10
const { user, session, error } = await supabase.auth.signIn({
_10
email: 'example@email.com',
_10
password: 'example-password',
_10
})


Sign out a user


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

_10
const { data, error } = await supabase.auth.update({
_10
password: new_password,
_10
})


_10
const { data, error } = await supabase.auth.api.resetPasswordForEmail(
_10
email,
_10
{ redirectTo: 'https://example.com/update-password' }
_10
)


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.


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


Update the access token


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


Retrieve a user

This method gets the user object from memory.


_10
const user = supabase.auth.user()


Retrieve a session


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

_10
const { user, error } = await supabase.auth.api.getUser(
_10
'ACCESS_TOKEN_JWT',
_10
)


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<Union: expand to see options>

_10
const { data, error } = await supabase.functions.invoke('hello', {
_10
body: JSON.stringify({ foo: 'bar' })
_10
})


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;

_10
const mySubscription = supabase
_10
.from('*')
_10
.on('*', payload => {
_10
console.log('Change received!', payload)
_10
})
_10
.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.

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

_10
supabase.removeAllSubscriptions()


Retrieve subscriptions


_10
const subscriptions = supabase.getSubscriptions()


Create a bucket

  • Policy permissions required:
    • buckets permissions: insert
    • objects permissions: none

_10
const { data, error } = await supabase
_10
.storage
_10
.createBucket('avatars', { public: false })


Retrieve a bucket

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: none

_10
const { data, error } = await supabase
_10
.storage
_10
.getBucket('avatars')


List all buckets

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: none

_10
const { data, error } = await supabase
_10
.storage
_10
.listBuckets()


Update a bucket

  • Policy permissions required:
    • buckets permissions: update
    • objects permissions: none

_10
const { data, error } = await supabase
_10
.storage
_10
.updateBucket('avatars', { public: false })


Delete a bucket

  • Policy permissions required:
    • buckets permissions: select and delete
    • objects permissions: none

_10
const { data, error } = await supabase
_10
.storage
_10
.deleteBucket('avatars')


Empty a bucket

  • Policy permissions required:
    • buckets permissions: select
    • objects permissions: select and delete

_10
const { data, error } = await supabase
_10
.storage
_10
.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.

_10
const avatarFile = event.target.files[0]
_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.upload('public/avatar1.png', avatarFile, {
_10
cacheControl: '3600',
_10
upsert: false
_10
})


Download a file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.download('folder/avatar1.png')


List all files in a bucket

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.list('folder', {
_10
limit: 100,
_10
offset: 0,
_10
sortBy: { column: 'name', order: 'asc' },
_10
})


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.

_10
const avatarFile = event.target.files[0]
_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.update('public/avatar1.png', avatarFile, {
_10
cacheControl: '3600',
_10
upsert: false
_10
})


Move an existing file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: update and select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.move('public/avatar1.png', 'private/avatar2.png')


Copy an existing file

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: insert and select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.copy('public/avatar1.png', 'private/avatar2.png')


Delete files in a bucket

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: delete and select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.remove(['folder/avatar1.png'])


Create a signed URL

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select

_10
const { signedURL, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.createSignedUrl('folder/avatar1.png', 60)


Create signed URLs

  • Policy permissions required:
    • buckets permissions: none
    • objects permissions: select

_10
const { data, error } = await supabase
_10
.storage
_10
.from('avatars')
_10
.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

_10
const { publicURL, error } = supabase
_10
.storage
_10
.from('public-bucket')
_10
.getPublicUrl('folder/avatar1.png')