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
- supabaseUrlstring
The unique Supabase URL which is supplied when you create a new project in your project dashboard.
- supabaseKeystring
The unique Supabase Key which is supplied when you create a new project in your project dashboard.
- optionsOptionalSupabaseClientOptions
1import { createClient } from '@supabase/supabase-js'23 // Create a single supabase client for interacting with your database4 const supabase = createClient('https://xyzcompany.supabase.co', 'publishable-or-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
1npm install @supabase/supabase-js@2Optionally if you are using custom configuration with createClient then follow below:
1const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {2 schema: 'custom',3 persistSession: false,4})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
1const { user, error } = await supabase2 .auth3 .signIn({ email, password })Sign in with magic link
1const { error } = await supabase2 .auth3 .signIn({ email })Sign in with a third-party provider
1const { error } = await supabase2 .auth3 .signIn({ provider })Sign in with phone
1const { error } = await supabase2 .auth3 .signIn({ phone, password })Sign in with phone using OTP
1const { error } = await supabase2 .auth3 .api4 .sendMobileOTP(phone)Reset password for email
1const { data, error } = await supabase2 .auth3 .api4 .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.
1const session = supabase.auth.session()Get the logged-in user
1const user = supabase.auth.user()Update user data for a logged-in user
1const { user, error } = await supabase2 .auth3 .update({ attributes })Use a custom access_token JWT with Supabase
1const { 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
1const { data, error } = await supabase2 .from('my_table')3 .insert({ new_data })Update and return data
1const { data, error } = await supabase2 .from('my_table')3 .update({ new_data })4 .eq('id', id)Upsert and return data
1const { data, error } = await supabase2 .from('my_table')3 .upsert({ new_data })Delete and return data
1const { data, error } = await supabase2 .from('my_table')3 .delete()4 .eq('id', id)Realtime methods
Subscribe
1const userListener = supabase2 .from('users')3 .on('*', (payload) => handleAllEventsPayload(payload.new))4 .subscribe()Unsubscribe
1userListener.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
apikeyis technically a reserved keyword, since the API gateway will pluck it out for authentication. It should be avoided as a column name.
1const { data, error } = await supabase2 .from('cities')3 .select()Insert data
- By default, every time you run
insert(), the client library will make aselectto return the full record. This is convenient, but it can also cause problems if your Policies are not configured to allow theselectoperation. If you are using Row Level Security and you are encountering problems, try setting thereturningparam tominimal.
1const { data, error } = await supabase2 .from('cities')3 .insert([4 { name: 'The Shire', country_id: 554 }5 ])Update data
update()should always be combined with Filters to target the item(s) you wish to update.
1const { data, error } = await supabase2 .from('characters')3 .update({ name: 'Han Solo' })4 .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.
1const { data, error } = await supabase2 .from('messages')3 .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 throughSELECTpolicies are deleted. Note that by default no rows are visible, so you need at least oneSELECT/ALLpolicy that makes the rows visible.
1const { data, error } = await supabase2 .from('cities')3 .delete()4 .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.
1create or replace function hello_world() returns text as $$2 select 'Hello world';3$$ language sql;1const { data, error } = await supabase2 .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:
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .eq('name', 'The Shire') // Correct56const { data, error } = await supabase7 .from('cities')8 .eq('name', 'The Shire') // Incorrect9 .select('name, country_id')Chaining
Filters can be chained together to produce advanced queries. For example:
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .gte('population', 1000)5 .lt('population', 10000)Conditional Chaining
Filters can be built up one step at a time and then executed. For example:
1const filterByName = null2const filterPopLow = 10003const filterPopHigh = 1000045let query = supabase6 .from('cities')7 .select('name, country_id')89if (filterByName) { query = query.eq('name', filterByName) }10if (filterPopLow) { query = query.gte('population', filterPopLow) }11if (filterPopHigh) { query = query.lt('population', filterPopHigh) }1213const { data, error } = await queryColumn is equal to a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .eq('name', 'The shire')Column is not equal to a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .neq('name', 'The shire')Column is greater than a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .gt('country_id', 250)Column is greater than or equal to a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .gte('country_id', 250)Column is less than a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .lt('country_id', 250)Column is less than or equal to a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .lte('country_id', 250)Column matches a pattern
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .like('name', '%la%')Column matches a case-insensitive pattern
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .ilike('name', '%la%')Column is a value
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .is('name', null)Column is in an array
1const { data, error } = await supabase2 .from('characters')3 .select('name, book_id')4 .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.contains('arraycol',["a","b"]) // You can use a javascript array for an array column2.contains('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.3.contains('rangecol','(1,2]') // Use Postgres range syntax for range column.4.contains('rangecol',`(${arr}]`) // You can insert an array into a string.
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, main_exports')4 .contains('main_exports', ['oil'])Contained by value
-
.containedBy()can work on array columns or range columns.1.containedBy('arraycol',["a","b"]) // You can use a javascript array for an array column2.containedBy('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.3.containedBy('rangecol','(1,2]') // Use Postgres range syntax for range column.4.containedBy('rangecol',`(${arr}]`) // You can insert an array into a string.
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, main_exports')4 .containedBy('main_exports', ['cars', 'food', 'machine'])Greater than a range
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, population_range_millions')4 .rangeGt('population_range_millions', '[150, 250]')Greater than or equal to a range
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, population_range_millions')4 .rangeGte('population_range_millions', '[150, 250]')Less than or equal to a range
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, population_range_millions')4 .rangeLt('population_range_millions', '[150, 250]')Mutually exclusive to a range
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, population_range_millions')4 .rangeAdjacent('population_range_millions', '[70, 185]')With a common element
-
.overlaps()can work on array columns or range columns.1.overlaps('arraycol',["a","b"]) // You can use a javascript array for an array column2.overlaps('arraycol','{"a","b"}') // You can use a string with Postgres array {} for array column.3.overlaps('rangecol','(1,2]') // Use Postgres range syntax for range column.4.overlaps('rangecol',`(${arr}]`) // You can insert an array into a string.
1const { data, error } = await supabase2 .from('countries')3 .select('name, id, main_exports')4 .overlaps('main_exports', ['computers', 'minerals'])Match a string
1const { data, error } = await supabase2 .from('quotes')3 .select('catchphrase')4 .textSearch('catchphrase', `'fat' & 'cat'`, {5 config: 'english'6 })Match an associated value
1const { data, error } = await supabase2 .from('characters')3 .select('name, book_id')4 .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.not('name','eq','Luke')2.not('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains.3.not('rangecol','cs','(1,2]') // Use Postgres range syntax for range column.4.not('id','in','(6,7)') // Use Postgres list () for in filter.5.not('id','in',`(${arr})`) // You can insert a javascript array.
1const { data, error } = await supabase2 .from('countries')3 .select('name, country_id')4 .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.or('id.in.(6,7), arraycol.cs.{"a","b"}') // Use Postgres list () for in filter. Array {} for array column and 'cs' for contains.2.or(`id.in.(${arrList}),arraycol.cs.{${arr}}`) // You can insert a javascipt array for list or array on array column.3.or(`id.in.(${arrList}),rangecol.cs.[${arrRange})`) // You can insert a javascipt array for list or range on a range column.
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .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.filter('arraycol','cs','{"a","b"}') // Use Postgres array {} for array column and 'cs' for contains.2.filter('rangecol','cs','(1,2]') // Use Postgres range syntax for range column.3.filter('id','in','(6,7)') // Use Postgres list () for in filter.4.filter('id','in',`(${arr})`) // You can insert a javascript array.
1const { data, error } = await supabase2 .from('characters')3 .select('name, book_id')4 .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
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .order('id', { ascending: false })Limit the number of rows returned
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .limit(1)Limit the query to a range
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .range(0,3)Retrieve one row of data
1const { data, error } = await supabase2 .from('cities')3 .select('name, country_id')4 .limit(1)5 .single()Retrieve zero or one row of data
1const { data, error } = await supabase2 .from('instruments')3 .select('name, section_id')4 .eq('name', 'violin')5 .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
useris returned butsessionwill be null - If "Email Confirmations" is turned off, both a
userand asessionwill 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().
1const { user, session, error } = await supabase.auth.signUp({2 email: 'example@email.com',3 password: 'example-password',4})Listen to auth events
1supabase.auth.onAuthStateChange((event, session) => {2 console.log(event, session)3})Sign in a user
- A user can sign up either via email or OAuth.
- If you provide
emailwithout 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
providerwill open the browser to the relevant login page.
1const { user, session, error } = await supabase.auth.signIn({2 email: 'example@email.com',3 password: 'example-password',4})Sign out a user
1const { 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
redirectToparam. 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():
1const { data, error } = await supabase.auth.update({2 password: new_password,3})1const { data, error } = await supabase.auth.api.resetPasswordForEmail(2 email,3 { redirectTo: 'https://example.com/update-password' }4)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.
1const { user, error } = await supabase.auth.update({email: 'new@email.com'})Update the access token
1function apiFunction(req, res) {2 // Assuming the access token was sent as a header "X-Supabase-Auth"3 const { access_token } = req.get('X-Supabase-Auth')45 // You can now use it within a Supabase Client6 const supabase = createClient("https://xyzcompany.supabase.co", "publishable-or-anon-key")7 const { user, error } = supabase.auth.setAuth(access_token)89 // This client will now send requests as this user10 const { data } = await supabase.from('your_table').select()11}Retrieve a user
This method gets the user object from memory.
1const user = supabase.auth.user()Retrieve a session
1const 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.
1const { user, error } = await supabase.auth.api.getUser(2 'ACCESS_TOKEN_JWT',3)Invokes a Supabase Edge Function.
invoke(functionName, options)Invokes a function
Invokes a Supabase Edge Function.
- Requires an Authorization header.
- Invoke params generally match the Fetch API spec.
Parameters
- functionNamestring
The name of the Function to invoke.
- optionsFunctionInvokeOptions
Options for invoking the Function.
Return Type
- Option 1FunctionsResponseSuccess
- Option 2FunctionsResponseFailure
1const { data, error } = await supabase.functions.invoke('hello', {2 body: JSON.stringify({ foo: 'bar' })3})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 IDENTITYtoFULL, like this:ALTER TABLE your_table REPLICA IDENTITY FULL;
1const mySubscription = supabase2 .from('*')3 .on('*', payload => {4 console.log('Change received!', payload)5 })6 .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.
1supabase.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.
1supabase.removeAllSubscriptions()Retrieve subscriptions
1const subscriptions = supabase.getSubscriptions()File Buckets
This section contains methods for working with File Buckets.
List all buckets
- Policy permissions required:
bucketspermissions:selectobjectspermissions: none
1const { data, error } = await supabase2 .storage3 .listBuckets()Retrieve a bucket
- Policy permissions required:
bucketspermissions:selectobjectspermissions: none
1const { data, error } = await supabase2 .storage3 .getBucket('avatars')Create a bucket
- Policy permissions required:
bucketspermissions:insertobjectspermissions: none
1const { data, error } = await supabase2 .storage3 .createBucket('avatars', { public: false })Empty a bucket
- Policy permissions required:
bucketspermissions:selectobjectspermissions:selectanddelete
1const { data, error } = await supabase2 .storage3 .emptyBucket('avatars')Update a bucket
- Policy permissions required:
bucketspermissions:updateobjectspermissions: none
1const { data, error } = await supabase2 .storage3 .updateBucket('avatars', { public: false })Delete a bucket
- Policy permissions required:
bucketspermissions:selectanddeleteobjectspermissions: none
1const { data, error } = await supabase2 .storage3 .deleteBucket('avatars')Upload a file
- Policy permissions required:
bucketspermissions: noneobjectspermissions:insert
- For React Native, using either
Blob,FileorFormDatadoes not work as intended. Upload file usingArrayBufferfrom base64 file data instead, see example below.
1const avatarFile = event.target.files[0]2const { data, error } = await supabase3 .storage4 .from('avatars')5 .upload('public/avatar1.png', avatarFile, {6 cacheControl: '3600',7 upsert: false8 })Replace an existing file
- Policy permissions required:
bucketspermissions: noneobjectspermissions:updateandselect
- For React Native, using either
Blob,FileorFormDatadoes not work as intended. Update file usingArrayBufferfrom base64 file data instead, see example below.
1const avatarFile = event.target.files[0]2const { data, error } = await supabase3 .storage4 .from('avatars')5 .update('public/avatar1.png', avatarFile, {6 cacheControl: '3600',7 upsert: false8 })Move an existing file
- Policy permissions required:
bucketspermissions: noneobjectspermissions:updateandselect
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .move('public/avatar1.png', 'private/avatar2.png')Copy an existing file
- Policy permissions required:
bucketspermissions: noneobjectspermissions:insertandselect
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .copy('public/avatar1.png', 'private/avatar2.png')Create a signed URL
- Policy permissions required:
bucketspermissions: noneobjectspermissions:select
1const { signedURL, error } = await supabase2 .storage3 .from('avatars')4 .createSignedUrl('folder/avatar1.png', 60)Create signed URLs
- Policy permissions required:
bucketspermissions: noneobjectspermissions:select
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .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:
bucketspermissions: noneobjectspermissions: none
1const { publicURL, error } = supabase2 .storage3 .from('public-bucket')4 .getPublicUrl('folder/avatar1.png')Download a file
- Policy permissions required:
bucketspermissions: noneobjectspermissions:select
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .download('folder/avatar1.png')Delete files in a bucket
- Policy permissions required:
bucketspermissions: noneobjectspermissions:deleteandselect
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .remove(['folder/avatar1.png'])List all files in a bucket
- Policy permissions required:
bucketspermissions: noneobjectspermissions:select
1const { data, error } = await supabase2 .storage3 .from('avatars')4 .list('folder', {5 limit: 100,6 offset: 0,7 sortBy: { column: 'name', order: 'asc' },8 })