JavaScript Client Library
@supabase/supabase-jsView on GitHubThis 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
_10npm install @supabase/supabase-js@2
Optionally if you are using custom configuration with createClient
then follow below:
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
_10const { user, error } = await supabase_10 .auth_10 .signIn({ email, password })
Sign in with magic link
_10const { error } = await supabase_10 .auth_10 .signIn({ email })
Sign in with a third-party provider
_10const { error } = await supabase_10 .auth_10 .signIn({ provider })
Sign in with phone
_10const { error } = await supabase_10 .auth_10 .signIn({ phone, password })
Sign in with phone using OTP
_10const { error } = await supabase_10 .auth_10 .api_10 .sendMobileOTP(phone)
Reset password for email
_10const { data, error } = await supabase_10 .auth_10 .api_10 .resetPasswordForEmail(email)
Get the user's current session
Note that auth.getSession
reads the auth token and the unencoded session data from the local storage medium. It doesn't send a request back to the Supabase Auth server unless the local session is expired.
You should never trust the unencoded session data if you're writing server code, since it could be tampered with by the sender. If you need verified, trustworthy user data, call auth.getUser
instead, which always makes a request to the Auth server to fetch trusted data.
_10const session = supabase.auth.session()
Get the logged-in user
_10const user = supabase.auth.user()
Update user data for a logged-in user
_10const { user, error } = await supabase_10 .auth_10 .update({ attributes })
Use a custom access_token
JWT with Supabase
_10const { user, error } = supabase.auth.setAuth(access_token)
Cookie methods
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
_10const { data, error } = await supabase_10 .from('my_table')_10 .insert({ new_data })
Update and return data
_10const { data, error } = await supabase_10 .from('my_table')_10 .update({ new_data })_10 .eq('id', id)
Upsert and return data
_10const { data, error } = await supabase_10 .from('my_table')_10 .upsert({ new_data })
Delete and return data
_10const { data, error } = await supabase_10 .from('my_table')_10 .delete()_10 .eq('id', id)
Realtime methods
Subscribe
_10const userListener = supabase_10 .from('users')_10 .on('*', (payload) => handleAllEventsPayload(payload.new))_10 .subscribe()
Unsubscribe
_10userListener.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 Modifiersselect()
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.
_10const { data, error } = await supabase_10 .from('cities')_10 .select()
Insert data
- By default, every time you run
insert()
, the client library will make aselect
to return the full record. This is convenient, but it can also cause problems if your Policies are not configured to allow theselect
operation. If you are using Row Level Security and you are encountering problems, try setting thereturning
param tominimal
.
_10const { 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.
_10const { 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.
_10const { 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 throughSELECT
policies are deleted. Note that by default no rows are visible, so you need at least oneSELECT
/ALL
policy that makes the rows visible.
_10const { 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.
_10create or replace function hello_world() returns text as $$_10 select 'Hello world';_10$$ language sql;
_10const { 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:
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .eq('name', 'The Shire') // Correct_10_10const { 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:
_10const { 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:
_13const filterByName = null_13const filterPopLow = 1000_13const filterPopHigh = 10000_13_13let query = supabase_13 .from('cities')_13 .select('name, country_id')_13_13if (filterByName) { query = query.eq('name', filterByName) }_13if (filterPopLow) { query = query.gte('population', filterPopLow) }_13if (filterPopHigh) { query = query.lt('population', filterPopHigh) }_13_13const { data, error } = await query
Column is equal to a value
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .eq('name', 'The shire')
Column is not equal to a value
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .neq('name', 'The shire')
Column is greater than a value
_10const { 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
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .gte('country_id', 250)
Column is less than a value
_10const { 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
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .lte('country_id', 250)
Column matches a pattern
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .like('name', '%la%')
Column matches a case-insensitive pattern
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .ilike('name', '%la%')
Column is a value
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .is('name', null)
Column is in an array
_10const { 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.
_10const { 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.
_10const { data, error } = await supabase_10 .from('countries')_10 .select('name, id, main_exports')_10 .containedBy('main_exports', ['cars', 'food', 'machine'])
Greater than a range
_10const { 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
_10const { 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
_10const { 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
_10const { 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.
_10const { data, error } = await supabase_10 .from('countries')_10 .select('name, id, main_exports')_10 .overlaps('main_exports', ['computers', 'minerals'])
Match a string
_10const { data, error } = await supabase_10 .from('quotes')_10 .select('catchphrase')_10 .textSearch('catchphrase', `'fat' & 'cat'`, {_10 config: 'english'_10 })
Match an associated value
_10const { 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.
_10const { 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.
_10const { 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.
_10const { 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
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .order('id', { ascending: false })
Limit the number of rows returned
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .limit(1)
Limit the query to a range
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .range(0,3)
Retrieve one row of data
_10const { data, error } = await supabase_10 .from('cities')_10 .select('name, country_id')_10 .limit(1)_10 .single()
Retrieve zero or one row of data
_10const { 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 butsession
will be null - If "Email Confirmations" is turned off, both a
user
and asession
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().
_10const { user, session, error } = await supabase.auth.signUp({_10 email: 'example@email.com',_10 password: 'example-password',_10})
Listen to auth events
_10supabase.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 apassword
, 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.
_10const { user, session, error } = await supabase.auth.signIn({_10 email: 'example@email.com',_10 password: 'example-password',_10})
Sign out a user
_10const { 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()
:
_10const { data, error } = await supabase.auth.update({_10 password: new_password,_10})
_10const { 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.
_10const { user, error } = await supabase.auth.update({email: 'new@email.com'})
Update the access token
_11function 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.
_10const user = supabase.auth.user()
Retrieve a session
_10const 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.
_10const { 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
_10const { 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
toFULL
, like this:ALTER TABLE your_table REPLICA IDENTITY FULL;
_10const 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.
_10supabase.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.
_10supabase.removeAllSubscriptions()
Retrieve subscriptions
_10const subscriptions = supabase.getSubscriptions()
Create a bucket
- Policy permissions required:
buckets
permissions:insert
objects
permissions: none
_10const { data, error } = await supabase_10 .storage_10 .createBucket('avatars', { public: false })
Retrieve a bucket
- Policy permissions required:
buckets
permissions:select
objects
permissions: none
_10const { data, error } = await supabase_10 .storage_10 .getBucket('avatars')
List all buckets
- Policy permissions required:
buckets
permissions:select
objects
permissions: none
_10const { data, error } = await supabase_10 .storage_10 .listBuckets()
Update a bucket
- Policy permissions required:
buckets
permissions:update
objects
permissions: none
_10const { data, error } = await supabase_10 .storage_10 .updateBucket('avatars', { public: false })
Delete a bucket
- Policy permissions required:
buckets
permissions:select
anddelete
objects
permissions: none
_10const { data, error } = await supabase_10 .storage_10 .deleteBucket('avatars')
Empty a bucket
- Policy permissions required:
buckets
permissions:select
objects
permissions:select
anddelete
_10const { data, error } = await supabase_10 .storage_10 .emptyBucket('avatars')
Upload a file
- Policy permissions required:
buckets
permissions: noneobjects
permissions:insert
- For React Native, using either
Blob
,File
orFormData
does not work as intended. Upload file usingArrayBuffer
from base64 file data instead, see example below.
_10const avatarFile = event.target.files[0]_10const { 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: noneobjects
permissions:select
_10const { 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: noneobjects
permissions:select
_10const { 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: noneobjects
permissions:update
andselect
- For React Native, using either
Blob
,File
orFormData
does not work as intended. Update file usingArrayBuffer
from base64 file data instead, see example below.
_10const avatarFile = event.target.files[0]_10const { 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: noneobjects
permissions:update
andselect
_10const { 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: noneobjects
permissions:insert
andselect
_10const { 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: noneobjects
permissions:delete
andselect
_10const { data, error } = await supabase_10 .storage_10 .from('avatars')_10 .remove(['folder/avatar1.png'])
Create a signed URL
- Policy permissions required:
buckets
permissions: noneobjects
permissions:select
_10const { signedURL, error } = await supabase_10 .storage_10 .from('avatars')_10 .createSignedUrl('folder/avatar1.png', 60)
Create signed URLs
- Policy permissions required:
buckets
permissions: noneobjects
permissions:select
_10const { 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: noneobjects
permissions: none
_10const { publicURL, error } = supabase_10 .storage_10 .from('public-bucket')_10 .getPublicUrl('folder/avatar1.png')