fix(docs) Resolve 196 mdx lint warnings for just, quickly, actually, PostgreSQL (#47358)

Closes DOCS-1057
Contributes to DOCS-1052

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## Problem

We have hundreds of MDX lint warnings in our docs going against style
best practices.

## Solution

Remove and replace in context the following:

- PostgreSQL. There was only one. There was concern about exceptions,
but I found none.
- Just
- Quickly
- Actually

### What changed

Edits follow the [Google developer documentation style
guide](https://developers.google.com/style): concise, direct, active
voice. The flagged words were removed when the sentence still read well,
or replaced when meaning needed to be preserved.

### Common patterns

| Flagged word | Approach | Example |
|---|---|---|
| **just** (filler) | Removed | "you just installed" → "you installed" |
| **just** (limiting) | **only** | "just one row" → "only one row" |
| **just like** | **like** / **the same as** | "function just like
regular users" → "function like regular users" |
| **not just** | **not only** | "not just errors" → "not only errors" |
| **quickly** (performance) | **efficiently** or removed | "find rows
quickly" → "find rows efficiently" |
| **quickly** (time) | **soon** / **rapidly** / removed | "expires too
quickly" → "expires too soon" |
| **actually** (filler) | Removed | "actually execute" → "execute"; "is
actually the most common" → "is the most common" |

## Tophatting

1. See the diff.
2. See that content continues to make sense in context.
3. Locally, `cd apps/docs` and run `pnpm run lint:mdx`.
4. Search for "just," "actually," "quickly", and "PostgreSQL" and see
there are 0 warnings.




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated wording across quickstarts, guides, and troubleshooting
articles for grammar, clarity, and consistent step-by-step phrasing.
* Clarified key concepts including Row Level Security policy evaluation
across Supabase products, deferred foreign key constraint behavior, and
when `EXPLAIN ANALYZE` executes queries (and related side effects).
* Refined several troubleshooting instructions and added guidance to cap
log payload size to reduce billed Logs Ingest volume.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nik Richers <nrichers@gmail.com>
Co-authored-by: Chris Chinchilla <chris.ward@supabase.io>
This commit is contained in:
authored and GitHub committed 2026-06-29 09:40:25 -07:00
1 parent 9b3d57f05c
commit 3dffdefd6e
123 files changed
+206 -206

No files matched your search

@@ -2,4 +2,4 @@
If you have your own infrastructure for deploying Python apps, you can continue to use `vecs` as described in this guide.
Alternatively if you would like to quickly deploy using Supabase, check out our guide on using the [Hugging Face Inference API](/docs/guides/ai/hugging-face) in Edge Functions using TypeScript.
Alternatively if you would like to deploy using Supabase, check out our guide on using the [Hugging Face Inference API](/docs/guides/ai/hugging-face) in Edge Functions using TypeScript.
@@ -22,7 +22,7 @@ on todos for select
using ( set_information() AND (select auth.uid()) = user_id );
```
This ensures the function is called when evaluating RLS policies for all products, not just Data API requests.
This ensures the function is called when evaluating RLS policies for all products, not only Data API requests.
**Performance consideration:**
@@ -10,7 +10,7 @@ Before building, you must set up your Database and API with a new Project in Sup
### Set up the database schema
Now we are going to set up the database schema. You can just copy/paste the SQL from below and run it yourself.
Now we are going to set up the database schema. You can copy/paste the SQL from below and run it yourself.
<Tabs
scrollable
@@ -25,7 +25,7 @@
Add the Postgres binary to your system PATH.
In Control Panel, under the Advanced tab of System Properties, click Environment Variables. Edit the Path variable by adding the path to the SQL binary you just installed.
In Control Panel, under the Advanced tab of System Properties, click Environment Variables. Edit the Path variable by adding the path to the SQL binary you installed.
The path will look something like this, though it may differ slightly depending on your installed version:
@@ -82,13 +82,13 @@ To start quicker you may use Supabase CLI to spin everything up locally as it al
supabase start
```
This will pull all docker images and run Supabase stack in docker on your local machine. It will also apply all the necessary migrations to set the whole thing up. You can then use your local setup the same way, just export the environment variables and follow to the next steps.
This will pull all docker images and run Supabase stack in docker on your local machine. It will also apply all the necessary migrations to set the whole thing up. You can then use your local setup the same way: export the environment variables and follow to the next steps.
Using `supabase-cli` is not required and you can use any other docker image or hosted version of Postgres that includes `pgvector`. Just make sure you run migrations from `examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql`.
Using `supabase-cli` is not required and you can use any other docker image or hosted version of Postgres that includes `pgvector`. Make sure you run migrations from `examples/providers/supabase/migrations/20230414142107_init_pg_vector.sql`.
### Step 5: Obtain OpenAI API key
To create embeddings Plugin uses OpenAI API and `text-embedding-ada-002` model. Each time we add some data to our datastore, or try to query relevant information from it, embedding will be created either for inserted data chunk, or for the query itself. To make it work we need to export `OPENAI_API_KEY`. If you already have an account in OpenAI, you just need to go to [User Settings - API keys](https://platform.openai.com/account/api-keys) and Create new secret key.
To create embeddings Plugin uses OpenAI API and `text-embedding-ada-002` model. Each time we add some data to our datastore, or try to query relevant information from it, embedding will be created either for inserted data chunk, or for the query itself. To make it work we need to export `OPENAI_API_KEY`. If you already have an account in OpenAI, go to [User Settings - API keys](https://platform.openai.com/account/api-keys) and Create new secret key.
![OpenAI Secret Keys](/docs/img/ai/chatgpt-plugins/openai-secret-keys.png)
@@ -6,7 +6,7 @@ breadcrumb: 'AI Examples'
Supabase provides a [Headless Search Toolkit](https://github.com/supabase/headless-vector-search) for adding "Generative Q&A" to your documentation. The toolkit is "headless", so that you can integrate it into your existing website and style it to match your website theme.
You can see how this works with the Supabase docs. Just hit `cmd+k` and "ask" for something like "what are the features of Supabase?". You will see that the response is streamed back, using the information provided in the docs:
You can see how this works with the Supabase docs. Enter `cmd+k` and ask, for example, "what are the features of Supabase?". You will see that the response is streamed back using the information provided in the docs:
![headless search](/docs/img/ai/headless-search/headless.png)
@@ -168,4 +168,4 @@ Go ahead and test it out by running `poetry run search` and you will be presente
## Conclusion
With just a couple of lines of Python you are able to implement image search as well as reverse image search using OpenAI's CLIP model and Supabase Vector.
With a couple of lines of Python you are able to implement image search as well as reverse image search using OpenAI's CLIP model and Supabase Vector.
@@ -175,4 +175,4 @@ You can now test it out by running `poetry run search`, and you will be presente
## Conclusion
With just a couple of Python scripts, you are able to implement video search as well as reverse video search using Mixpeek Embed and Supabase Vector. This approach allows for powerful semantic search capabilities that can be integrated into various applications, enabling you to search through video content using both text and video queries.
With a couple of Python scripts, you are able to implement video search as well as reverse video search using Mixpeek Embed and Supabase Vector. This approach allows for semantic search capabilities that can be integrated into various applications, enabling you to search through video content using both text and video queries.
@@ -231,4 +231,4 @@ Go ahead and test it out by running `poetry run search` and you will be presente
## Conclusion
With just a couple of lines of Python you are able to implement image search as well as reverse image search using the Amazon Titan multimodal model and Supabase Vector.
With a couple of lines of Python you are able to implement image search as well as reverse image search using the Amazon Titan multimodal model and Supabase Vector.
@@ -16,7 +16,7 @@ Hybrid search combines the strengths of both these methods. It would ensure that
## When to consider hybrid search
The decision to use hybrid search depends on what your users are looking for in your app. For a code repository where developers need to find exact lines of code or error messages, keyword search is likely ideal because it matches specific terms. In a mental health forum where users search for advice or experiences related to their feelings, semantic search may be better because it finds results based on the meaning of a query, not just specific words. For a shopping app where customers might search for specific product names yet also be open to related suggestions, hybrid search combines the best of both worlds - finding exact matches while also uncovering similar products based on the shopping context.
The decision to use hybrid search depends on what your users are looking for in your app. For a code repository where developers need to find exact lines of code or error messages, keyword search is likely ideal because it matches specific terms. In a mental health forum where users search for advice or experiences related to their feelings, semantic search may be better because it finds results based on the meaning of a query, not only specific words. For a shopping app where customers might search for specific product names yet also be open to related suggestions, hybrid search combines the best of both worlds - finding exact matches while also uncovering similar products based on the shopping context.
## How to combine search methods
@@ -46,7 +46,7 @@ This constant can be any positive number, but is typically small. A constant of
Implement hybrid search in Postgres using `tsvector` (keyword search) and `pgvector` (semantic search).
First we'll create a `documents` table to store the documents that we will search over. This is just an example - adjust this to match the structure of your application.
First, you can create a `documents` table to store the documents that you can search over. This is an example. Adjust this to match the structure of your application.
```sql
create table documents (
@@ -7,7 +7,7 @@ sidebar_label: 'Choosing a Client'
As described in [Structured & Unstructured Embeddings](/docs/guides/ai/structured-unstructured), AI workloads come in many forms.
For data science or ephemeral workloads, the [Supabase Vecs](https://supabase.github.io/vecs/) client gets you started quickly. All you need is a connection string and vecs handles setting up your database to store and query vectors with associated metadata.
For data science or ephemeral workloads, the [Supabase Vecs](https://supabase.github.io/vecs/) client gets you started. You need a connection string and vecs handles setting up your database to store and query vectors with associated metadata.
<Admonition type="tip">
@@ -59,7 +59,7 @@ create table documents (
);
```
In the above SQL snippet, we create a `documents` table with a column called `embedding` (note this is just a regular Postgres column - you can name it whatever you like). We give the `embedding` column a `vector` data type with 384 dimensions. Change this to the number of dimensions produced by your embedding model. For example, if you are [generating embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings) using the open source [`gte-small`](https://huggingface.co/Supabase/gte-small) model, you would set this number to 384 since that model produces 384 dimensions.
In the SQL snippet above, we create a `documents` table with an `embedding` column. This is a standard Postgres column, so you can name it anything you like. The `embedding` column uses the `vector` data type with 384 dimensions. Change this number to match the dimensions your embedding model produces. For example, if you're [generating embeddings](/docs/guides/ai/quickstarts/generate-text-embeddings) using the open source [`gte-small`](https://huggingface.co/Supabase/gte-small) model, set this to 384.
<Admonition type="tip">
@@ -73,6 +73,7 @@ In this example we'll generate a vector using Transformers.js, then store it in
```js
import { pipeline } from '@huggingface/transformers'
const generateEmbedding = await pipeline('feature-extraction', 'Supabase/gte-small')
const title = 'First post!'
@@ -110,7 +110,7 @@ The “navigable” part of NSW specifically refers to the ability to logarithmi
HNSW combines these two concepts. From the hierarchical perspective, the bottom layer consists of a NSW made up of short links between nodes. Each layer above “skips” elements and creates longer links between nodes further away from each other.
Just like skip lists, search starts at the top layer and works its way down until it finds the target element. However, instead of comparing a scalar value at each layer to determine whether or not to descend to the layer below, a multi-dimensional distance measure (such as Euclidean distance) is used.
Like skip lists, search starts at the top layer and works its way down until it finds the target element. However, instead of comparing a scalar value at each layer to determine whether or not to descend to the layer below, a multi-dimensional distance measure (such as Euclidean distance) is used.
## When should you create HNSW indexes?
@@ -4,7 +4,7 @@ title: 'Handling errors in `supabase-js`'
subtitle: 'Read `error.hint` first — Postgres often tells you the exact fix. Log the full error so you actually see it.'
---
Every `supabase-js` call returns a `{ data, error }` pair instead of throwing. When something fails, the single most useful field on `error` is usually `hint` — Postgres returns the _fix_, not just a description of the problem. Logging only `error.message` hides it.
Every `supabase-js` call returns a `{ data, error }` pair instead of throwing. When something fails, the single most useful field on `error` is usually `hint` — Postgres returns the _fix_, not only a description of the problem. Logging only `error.message` hides it.
## Usage of `message` and `hint` properties
@@ -19,7 +19,7 @@ The `message` exposes the error reason, and `hint` gives you the literal SQL sta
The same pattern shows up across many Postgres errors — missing column? `hint` suggests the column name you probably meant. Type mismatch? `hint` shows the expected type. Whenever Postgres knows the fix, it puts it in `hint`.
<Admonition type="tip">Log the full `error` object, not just `error.message`.</Admonition>
<Admonition type="tip">Log the full `error` object, not only `error.message`.</Admonition>
## The recommended pattern
@@ -8,7 +8,7 @@ subtitle: 'Create and use anonymous users to authenticate with Supabase'
<Admonition type="note" title="Anonymous user vs the anon key">
Calling `signInAnonymously()` creates an anonymous user. It's just like a permanent user, except the user can't access their account if they sign out, clear browsing data, or use another device.
Calling `signInAnonymously()` creates an anonymous user. It behaves like a permanent user, except the user can't access their account if they sign out, clear browsing data, or use another device.
Like permanent users, the `authenticated` Postgres role will be used when using the Data APIs to access your project. JWTs for these users will have an `is_anonymous` claim which you can use to distinguish in RLS policies.
@@ -279,7 +279,7 @@ response = supabase.auth.link_identity({'provider': 'google'})
## Access control
An anonymous user assumes the `authenticated` role just like a permanent user. You can use row-level security (RLS) policies to differentiate between an anonymous user and a permanent user by checking for the `is_anonymous` claim in the JWT returned by `auth.jwt()`:
An anonymous user assumes the `authenticated` role like a permanent user. You can use row-level security (RLS) policies to differentiate between an anonymous user and a permanent user by checking for the `is_anonymous` claim in the JWT returned by `auth.jwt()`:
```sql
create policy "Only permanent users can post to the news feed"
@@ -19,7 +19,7 @@ Supabase Auth will send a payload containing these fields to your hook:
<Admonition type="note">
Because the hook is ran just before the insertion into the database, this user will not be found in Postgres at the time the hook is called.
Because the hook runs immediately before insertion into the database, this user will not be found in Postgres at the time the hook is called.
</Admonition>
@@ -722,8 +722,8 @@ supabase functions new before-user-created-hook
Add the following code to your edge function:
```ts
import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0'
import { createClient } from 'https://esm.sh/@supabase/supabase-js'
import { Webhook } from 'https://esm.sh/standardwebhooks@1.0.0'
const whSecret = Deno.env.get('BEFORE_USER_CREATED_HOOK_SECRET')?.replace('v1,whsec_', '')
const supabaseUrl = Deno.env.get('SUPABASE_URL')
+2 -2
View File
@@ -271,7 +271,7 @@ It is possible to enforce MFA on the Server-Side Rendering level. However, this
You can use the `supabase.auth.mfa.getAuthenticatorAssuranceLevel()` and `supabase.auth.mfa.listFactors()` APIs to identify the AAL level of the session and any factors that are enabled for a user, similar to how you would use these on the browser.
However, encountering a different AAL level on the server may not actually be a security problem. Consider these likely scenarios:
However, encountering a different AAL level on the server may not be a security problem. Consider these likely scenarios:
1. User signed-in with a conventional method but closed their tab on the MFA
flow.
@@ -284,7 +284,7 @@ We thus recommend you redirect users to a page where they can authenticate using
### APIs
If your application uses the Supabase Database, Storage or Edge Functions, just using Row Level Security policies will give you sufficient protection. In the event that you have other APIs that you wish to protect, follow these general guidelines:
If your application uses the Supabase Database, Storage or Edge Functions, Row Level Security policies provide sufficient protection. In the event that you have other APIs that you wish to protect, follow these general guidelines:
1. **Use a good JWT verification and parsing library for your language.**
This will let you securely parse JWTs and extract their claims.
@@ -41,7 +41,7 @@ In the **login flow**, the user signs in (upgrading the session to AAL1) and the
An enrollment flow provides a UI for users to set up additional authentication factors. Most applications add the enrollment flow in two places within their app:
1. Right after login or sign up.
This allows users quickly set up Multi Factor Authentication (MFA) post login or account creation. Where possible, encourage all users to set up MFA. Many applications offer this as an opt-in step in an
This allows users to set up Multi Factor Authentication (MFA) post login or account creation. Where possible, encourage all users to set up MFA. Many applications offer this as an opt-in step in an
effort to reduce onboarding friction.
2. From within a settings page.
Allows users to set up, disable or modify their MFA settings.
@@ -47,7 +47,7 @@ In the **login flow**, the user signs in (upgrading the session to AAL1) and the
An enrollment flow provides a UI for users to set up additional authentication factors. Most applications add the enrollment flow in two places within their app:
1. Right after login or sign up.
This lets users quickly set up MFA immediately after they log in or create an
This lets users set up MFA immediately after they log in or create an
account. We recommend encouraging all users to set up MFA if that makes sense
for your application. Many applications offer this as an opt-in step in an
effort to reduce onboarding friction.
+1 -1
View File
@@ -118,7 +118,7 @@ This includes:
**Have another SMTP service set up on stand-by.**
In case the primary SMTP service you're using is experiencing difficulty, or your account is under threat of being blocked due to spam, you have another service to quickly turn to.
In case the primary SMTP service you're using is experiencing difficulty, or your account is under threat of being blocked due to spam, you have another service to turn to.
**Use consistent branding and focused content.**
@@ -120,7 +120,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This
- Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
- You need to enter your app redirect callback on `Additional Redirect URLs` field.
The redirect callback URL should have this format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.flutterquickstart://login-callback` is just an example, you can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
The redirect callback URL should have this format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.flutterquickstart://login-callback` is an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
![Supabase console deep link setting](/docs/img/deeplink-setting.png)
@@ -314,7 +314,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This
1. Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link.
The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
![Supabase console deep link setting](/docs/img/deeplink-setting.png)
@@ -356,7 +356,7 @@ With Deep Linking, you can configure this redirect to open a specific page. This
1. Go to your [auth settings](/dashboard/project/_/auth/url-configuration) page.
2. Enter your app redirect URL in the `Additional Redirect URLs` field. This is the URL that the user gets redirected to after clicking a magic link.
The redirect callback URL should have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. Here, `io.supabase.user-management://login-callback` is just an example. You can choose whatever you would like for `YOUR_SCHEME` and `YOUR_HOSTNAME` as long as the scheme is unique across the user's device. For this reason, typically a reverse domain of your website is used.
The redirect callback URL must have the format `[YOUR_SCHEME]://[YOUR_HOSTNAME]`. For example: `io.supabase.user-management://login-callback`. You can use any values for `YOUR_SCHEME` and `YOUR_HOSTNAME`, but the scheme must be unique across the user's device. For this reason, a reverse domain of your website is typically used.
Now, edit the Android manifest to make sure the app opens when the user clicks on the magic link.
@@ -3,7 +3,7 @@ title: 'OAuth 2.1 Server'
description: 'Turn your Supabase project into an OAuth 2.1 and OpenID Connect identity provider'
---
Supabase Auth can act as an OAuth 2.1 and OpenID Connect (OIDC) identity provider. This allows other applications and services to use your Supabase project as their authentication provider, just like "Sign in with Google" or "Sign in with GitHub".
Supabase Auth can act as an OAuth 2.1 and OpenID Connect (OIDC) identity provider. This allows other applications and services to use your Supabase project as their authentication provider, like "Sign in with Google" or "Sign in with GitHub".
You can use this to build "Sign in with [Your App]" experiences, authenticate AI agents through the Model Context Protocol (MCP), power developer platforms with third-party integrations, or implement standards-compliant enterprise SSO.
@@ -79,13 +79,13 @@ When building your own MCP server, integrate with Supabase Auth to authenticate
**Looking for an easier way to build MCP servers?**
[FastMCP](https://gofastmcp.com) provides a streamlined way to build MCP servers with built-in Supabase Auth integration. FastMCP handles OAuth configuration, token management, and authentication flows automatically, letting you focus on building your AI agent's functionality. Check out their [Supabase integration guide](https://gofastmcp.com/integrations/supabase#supabase-fastmcp) to get started quickly.
[FastMCP](https://gofastmcp.com) provides a streamlined way to build MCP servers with built-in Supabase Auth integration. FastMCP handles OAuth configuration, token management, and authentication flows automatically, letting you focus on building your AI agent's functionality. Check out their [Supabase integration guide](https://gofastmcp.com/integrations/supabase#supabase-fastmcp) to get started.
</Admonition>
## Handling MCP tokens in your application
When your MCP server makes requests to your Supabase APIs on behalf of authenticated users, it will send access tokens issued by Supabase Auth, just like any other OAuth client.
When your MCP server makes requests to your Supabase APIs on behalf of authenticated users, it will send access tokens issued by Supabase Auth, like any other OAuth client.
### Validating MCP tokens
@@ -151,7 +151,7 @@ This guarantee provides your application with close alignment with security comp
If you wish to make your own JWTs or have access to the private key or shared secret used by Supabase, you can create a new JWT signing key by importing a private key or setting a shared secret yourself.
Use the [Supabase CLI](/docs/reference/cli/introduction) to quickly and securely generate a private key ready for import:
Use the [Supabase CLI](/docs/reference/cli/introduction) to securely generate a private key ready for import:
```sh
supabase gen signing-key --algorithm ES256
@@ -228,7 +228,7 @@ This is to ensure you have the ability, should you need it, to go back to the le
### Why does revoking the legacy JWT secret require disabling of `anon` and `service_role` API keys?
Unfortunately `anon` and `service_role` are not just API keys, but are also valid JSON Web Tokens, signed by the legacy JWT secret. Revoking the legacy JWT secret means that your application no longer trusts any JWT signed with it. Therefore before you revoke the legacy JWT secret, you must disable the `anon` and `service_role` to ensure a consistent security setup.
Unfortunately `anon` and `service_role` are not only API keys, but are also valid JSON Web Tokens, signed by the legacy JWT secret. Revoking the legacy JWT secret means that your application no longer trusts any JWT signed with it. Therefore before you revoke the legacy JWT secret, you must disable the `anon` and `service_role` to ensure a consistent security setup.
### Using JWT-based `anon` key in a mobile, desktop, or CLI application and need to rotate a `service_role` JWT secret?
@@ -95,7 +95,7 @@ Configure this in the following way:
- Select the _App registrations_ menu in Microsoft Entra ID on the Azure portal.
- Select the OAuth app.
- Select the _Manifest_ menu in the sidebar.
- Make a backup of the JSON just in case.
- Make a backup of the JSON in case you need it later.
- Identify the `optionalClaims` key.
- Edit it by specifying the following object:
```json
+1 -1
View File
@@ -21,7 +21,7 @@ See the [Anonymous Signins guide](/docs/guides/auth/auth-anonymous) to learn mor
<Admonition type="caution" title="Anonymous users do not use the anon role">
Just like permanent users, anonymous users use the **authenticated** role for database access.
Like permanent users, anonymous users use the **authenticated** role for database access.
The **anon** role is for those who aren't signed in at all and are not tied to any user ID. We refer to these as unauthenticated or public users.
@@ -6,7 +6,7 @@ description: 'Quickly check if an index can be used without creating it.'
`HypoPG` is Postgres extension for creating hypothetical/virtual indexes. HypoPG allows users to rapidly create hypothetical/virtual indexes that have no resource cost (CPU, disk, memory) that are visible to the Postgres query planner.
The motivation for HypoPG is to allow users to quickly search for an index to improve a slow query without consuming server resources or waiting for them to build.
The motivation for HypoPG is to allow users to search for an index to improve a slow query without consuming server resources or waiting for them to build.
## Enable the extension
@@ -193,7 +193,7 @@ You can then assign the role to monitor only approved object events, such as `se
grant select on random_table to "some_audit_role";
```
With this privilege granted, PGAudit will record all select statements that reference the `random_table`, regardless of _who_ or _what_ actually initiated the event. All assignable privileges can be viewed in the [Postgres documentation](https://www.postgresql.org/docs/current/ddl-priv.html).
With this privilege granted, PGAudit will record all select statements that reference the `random_table`, regardless of _who_ or _what_ initiated the event. All assignable privileges can be viewed in the [Postgres documentation](https://www.postgresql.org/docs/current/ddl-priv.html).
If you would no longer like to use object logging, you will need to unassign the `pgaudit.role` variable:
@@ -124,7 +124,7 @@ id | content
### Match all search words
To find all memos where content contains BOTH of the words `postgres` and `pgroonga`, we can just use space to separate each words:
To find all memos where content contains BOTH of the words `postgres` and `pgroonga`, we can use space to separate each words:
{/* prettier-ignore */}
```sql
@@ -55,7 +55,7 @@ Procedural languages are automatically installed within `pg_catalog`, so you don
## Create `plv8` functions
Functions written in `plv8` are written just like any other Postgres functions, only
Functions written in `plv8` are written like any other Postgres functions, only
with the `language` identifier set to `plv8`.
```sql
@@ -192,7 +192,7 @@ val data = supabase.from("restaurants").insert(listOf(
</$Show>
</Tabs>
Notice the order in which you pass the latitude and longitude. Longitude comes first, and is because longitude represents the x-axis of the location. Another thing to watch for is when inserting data from the client library, there is no comma between the two values, just a single space.
Notice the order in which you pass the latitude and longitude. Longitude comes first, and is because longitude represents the x-axis of the location. Another thing to watch for is when inserting data from the client library, there is no comma between the two values, only a single space.
At this point, if you go into your Supabase dashboard and look at the data, you will notice that the value of the `location` column looks something like this.
@@ -65,7 +65,7 @@ where ts > (now() - interval '1 DAY');
This approach provides several benefits:
1. **Simplicity:** the Wrappers API is just SQL, so data engineers don't need to learn new tools and languages.
1. **Simplicity:** the Wrappers API is SQL, so data engineers don't need to learn new tools and languages.
1. **Save on time:** avoid setting up additional data pipelines.
1. **Save on Data Engineering costs:** less infrastructure to be managed.
@@ -5,7 +5,7 @@ title: 'Import data into Supabase'
You can import data into Supabase in multiple ways. The best method depends on your data size and app requirements.
If you're working with small datasets in development, you can experiment quickly using CSV import in the Supabase dashboard. If you're working with a large dataset in production, you should plan your data import to minimize app latency and ensure data integrity.
If you're working with small datasets in development, you can experiment with CSV import in the Supabase dashboard. If you're working with a large dataset in production, you should plan your data import to minimize app latency and ensure data integrity.
## How to import data into Supabase
@@ -250,9 +250,9 @@ Postgres has built in tooling to help you optimize poorly performing queries. Yo
explain analyze <query-statement-here>;
```
When you include `analyze` in the explain statement, the database attempts to execute the query and provides a detailed query plan along with actual execution times. So, be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will actually run, and could have unintended side-effects.
When you include `analyze` in the explain statement, the database attempts to execute the query and provides a detailed query plan along with actual execution times. So, be careful using `explain analyze` with `insert`/`update`/`delete` queries, because the query will run, and could have unintended side-effects.
If you run just `explain` without the `analyze` keyword, the database will only perform query planning without actually executing the query. This approach can be beneficial when you want to inspect the query plan without affecting the database or if you encounter timeouts in your queries.
If you run `explain` without the `analyze` keyword, the database will only perform query planning without executing the query. This approach can be beneficial when you want to inspect the query plan without affecting the database or if you encounter timeouts in your queries.
Using the query plan analyzer to optimize your queries is a large topic, with a number of online resources available:
+1 -1
View File
@@ -23,7 +23,7 @@ Don't go overboard with `json/jsonb` columns. They are a useful tool, but most o
## Create JSONB columns
`json/jsonb` is just another "data type" for Postgres columns. You can create a `jsonb` column in the same way you would create a `text` or `int` column:
`json/jsonb` is another "data type" for Postgres columns. You can create a `jsonb` column in the same way you would create a `text` or `int` column:
<Tabs
scrollable
@@ -62,7 +62,7 @@ height={2034}
### Creating tables
To create a table using the OrioleDB storage engine just execute the standard `CREATE TABLE` statement. By default it will create a table using OrioleDB storage engine. For example:
To create a table using the OrioleDB storage engine, execute the standard `CREATE TABLE` statement. By default it will create a table using OrioleDB storage engine. For example:
```sql
-- Create a table
@@ -32,7 +32,7 @@ When a foreign key constraint is defined with the option `RESTRICT`, it means th
When a foreign key constraint is defined with the option `NO ACTION`, it means that if a row in the parent table is deleted, the database will also raise an error and prevent the deletion of the row in the parent table. However unlike `RESTRICT`, `NO ACTION` has the option to defer the check using `INITIALLY DEFERRED`. This will only raise the above error _if_ the referenced rows still exist at the end of the transaction.
The difference from `RESTRICT` is that a constraint marked as `NO ACTION INITIALLY DEFERRED` is deferred until the end of the transaction, rather than running immediately. If, for example there is another foreign key constraint between the same tables marked as `CASCADE`, the cascade will occur first and delete the referenced rows, and no error will be thrown by the deferred constraint. Otherwise if there are still rows referencing the parent row by the end of the transaction, an error will be raised just like before. Just like `RESTRICT`, the database will not delete, update or set to NULL any rows in the referenced tables.
The difference from `RESTRICT` is that a constraint marked as `NO ACTION INITIALLY DEFERRED` is deferred until the end of the transaction, rather than running immediately. If, for example there is another foreign key constraint between the same tables marked as `CASCADE`, the cascade will occur first and delete the referenced rows, and no error will be thrown by the deferred constraint. Otherwise if there are still rows referencing the parent row by the end of the transaction, an error will be raised as before. Like `RESTRICT`, the database will not delete, update or set to NULL any rows in the referenced tables.
In practice, you can use either `NO ACTION` or `RESTRICT` depending on your needs. `NO ACTION` is the default behavior if you do not specify anything. If you prefer to defer the check until the end of the transaction, use `NO ACTION INITIALLY DEFERRED`.
@@ -4,7 +4,7 @@ title: 'Column Level Security'
description: 'Secure your data using Postgres Column Level Security.'
---
Postgres's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) gives you granular control over who can access rows of data. However, it doesn't give you control over which columns they can access within rows. Sometimes you want to restrict access to specific columns in your database. Column Level Privileges allows you to do just that.
Postgres's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) gives you granular control over who can access rows of data. However, it doesn't give you control over which columns they can access within rows. Sometimes you want to restrict access to specific columns in your database. Column Level Privileges allows you to do that.
<Admonition type="caution">
@@ -31,7 +31,7 @@ For example, assume you have a `posts` table with the following columns:
- `created_at`
- `updated_at`
You can restrict updates to just the user who created it using [RLS](/docs/guides/auth#row-level-security), with the following policy:
You can restrict updates to the user who created it using [RLS](/docs/guides/auth#row-level-security), with the following policy:
```sql
create policy "Allow update for owners" on posts for
@@ -66,7 +66,7 @@ update
(title, content) on table public.posts to authenticated;
```
In the above example, we are revoking the table-level `UPDATE` privilege from the `authenticated` role and granting a column-level `UPDATE` privilege on just the `title` and `content` columns.
In the above example, we are revoking the table-level `UPDATE` privilege from the `authenticated` role and granting a column-level `UPDATE` privilege on the `title` and `content` columns.
If we want to restrict access to updating the `title` column:
@@ -47,7 +47,7 @@ delete from logs
where created_at < now() - interval '90 days';
```
This acquires a `ROW EXCLUSIVE` lock on the table, which still allows other `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements to run concurrently. For small row counts, the operation completes quickly and has minimal impact.
This acquires a `ROW EXCLUSIVE` lock on the table, which still allows other `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements to run concurrently. For small row counts, the operation completes with minimal impact.
### Large deletes
@@ -188,7 +188,7 @@ If autovacuum is not keeping up, you can trigger a manual vacuum:
vacuum (verbose) logs;
```
For reclaiming disk space (not just marking tuples as reusable), use `VACUUM FULL` — but be aware this rewrites the entire table and takes an `ACCESS EXCLUSIVE` lock:
To reclaim disk space rather than only marking tuples as reusable, use `VACUUM FULL`. Note that this rewrites the entire table and takes an `ACCESS EXCLUSIVE` lock:
```sql
-- This locks the table for the duration — use during maintenance windows only
@@ -61,10 +61,10 @@ See how to [auto enable RLS for new tables](/docs/guides/database/postgres/row-l
Event triggers can be triggered on:
- `ddl_command_start` - occurs just before a DDL command for almost all objects within a schema
- `ddl_command_end` - occurs just after a DDL command for almost all objects within a schema
- `sql_drop` - occurs just before `ddl_command_end` for any DDL commands that `DROP` a database object (note that altering a table can cause it to be dropped)
- `table_rewrite` - occurs just before a table is rewritten using the `ALTER TABLE` command
- `ddl_command_start` - occurs before a DDL command for almost all objects within a schema
- `ddl_command_end` - occurs after a DDL command for almost all objects within a schema
- `sql_drop` - occurs before `ddl_command_end` for any DDL commands that `DROP` a database object (note that altering a table can cause it to be dropped)
- `table_rewrite` - occurs before a table is rewritten using the `ALTER TABLE` command
<Admonition type="caution">
@@ -5,7 +5,7 @@ footerHelpType: 'postgres'
tocVideo: 'bBu_V8CfWgM'
---
An index makes your Postgres queries faster. The index is like a "table of contents" for your data - a reference list which allows queries to quickly locate a row in a given table without needing to scan the entire table (which in large tables can take a long time).
An index makes your Postgres queries faster. The index is like a "table of contents" for your data - a reference list which allows queries to locate a row in a given table without needing to scan the entire table (which in large tables can take a long time).
Indexes can be structured in a few different ways. The type of index chosen depends on the values you are indexing. By far the most common index type, and the default in Postgres, is the B-Tree. A B-Tree is the generalized form of a binary search tree, where nodes can have more than two children.
@@ -67,7 +67,7 @@ Luckily Postgres provides us with `create index concurrently` which prevents blo
</Admonition>
Here is a simplified diagram of the index we just created (note that in practice, nodes actually have more than two children).
Here is a simplified diagram of the index we created (note that in practice, nodes have more than two children).
<Image
alt="B-Tree index example in Postgres"
@@ -140,7 +140,7 @@ Supabase maps every request to one of the roles:
- `anon`: an unauthenticated request (the user is not logged in)
- `authenticated`: an authenticated request (the user is logged in)
These are actually [Postgres Roles](/docs/guides/database/postgres/roles). You can use these roles within your Policies using the `TO` clause:
These are [Postgres Roles](/docs/guides/database/postgres/roles). You can use these roles within your Policies using the `TO` clause:
```sql
create policy "Profiles are viewable by everyone"
+1 -1
View File
@@ -6,7 +6,7 @@ breadcrumb: 'ORM Quickstarts'
hideToc: true
---
This quickly shows how to connect your Prisma application to Supabase Postgres. If you encounter any problems, reference the [Prisma troubleshooting docs](/docs/guides/database/prisma/prisma-troubleshooting).
This guide shows how to connect your Prisma application to Supabase Postgres. If you encounter any problems, reference the [Prisma troubleshooting docs](/docs/guides/database/prisma/prisma-troubleshooting).
<Admonition type="note">
@@ -72,7 +72,7 @@ Prisma is unable to allocate connections to pending queries fast enough to meet
#### Possible causes: [#possible-causes-timed-out-fetching-a-new-connection]
- **Overwhelmed server**: The server hosting Prisma is under heavy load, limiting its ability to manage connections. By default, Prisma will create the default `num_cpus * 2 + 1` worth of connections. A common cause for server strain is increasing the `connection_limit` significantly past the default.
- **Insufficient pool size**: The Supavisor pooler does not have enough connections available to quickly satisfy Prisma's requests.
- **Insufficient pool size**: The Supavisor pooler does not have enough connections available to satisfy Prisma's requests.
- **Slow queries**: Prisma's queries are taking too long to execute, preventing it from releasing connections for reuse.
#### Solutions: [#solution-timed-out-fetching-a-new-connection]
@@ -124,7 +124,7 @@ Postgres or Supavisor rejected a request for more connections
#### Solutions [#solutions-causes-max-client-connections-reached]
- **Transaction Mode for serverless apps**: If you are using serverless functions (Supabase Edge, Vercel, AWS Lambda), switch to transaction mode (port 6543). It handles more connections than session mode or direct connections.
- **Reduce the number of Prisma connections**: A single client-server can establish multiple connections with a pooler. Typically, serverless setups do not need many connections. Starting with fewer, like five or three, or even just one, is often sufficient. In serverless setups, begin with `connection_limit=1`, increasing cautiously if needed to avoid maxing out connections.
- **Reduce the number of Prisma connections**: A single client-server can establish multiple connections with a pooler. Typically, serverless setups do not need many connections. Starting with fewer, like five or three, or even one, is often sufficient. In serverless setups, begin with `connection_limit=1`, increasing cautiously if needed to avoid maxing out connections.
- **Increase pool size**: If you are connecting with Supavisor, try increasing the pool size in the [Database Settings](/dashboard/project/_/database/settings).
- **Disconnect appropriately**: Close Prisma connections when they are no longer needed.
- **Decrease query time**: Reduce query complexity or add [strategic indexes](/docs/guides/database/postgres/indexes) to your tables to speed up queries.
+1 -1
View File
@@ -579,7 +579,7 @@ from
where courses.code != 'PG101';
```
Without a view, we would need to go into every dependent query to add the new rule. This would increase in the likelihood of errors and inconsistencies, as well as introducing a lot of effort for a developer. With views, we can alter just the underlying query in the view **transcripts**. The change will be applied to all applications using this view.
Without a view, we would need to go into every dependent query to add the new rule. This would increase in the likelihood of errors and inconsistencies, as well as introducing a lot of effort for a developer. With views, we can alter the underlying query in the view **transcripts**. The change will be applied to all applications using this view.
#### Logical organization
+1 -1
View File
@@ -127,7 +127,7 @@ You should ensure that you protect access to this view with the appropriate SQL
### Updating secrets
A secret can be updated with the `vault.update_secret()` function, this function makes updating secrets easy, just provide the secret UUID as the first argument, and then an updated secret, updated optional unique name, or updated description:
To update a secret, use the `vault.update_secret()` function. Provide the secret UUID as the first argument, followed by an updated secret, name, or description:
```sql
select
@@ -12,7 +12,7 @@ You can hook into three table events: `INSERT`, `UPDATE`, and `DELETE`. All even
## Webhooks vs triggers
Database Webhooks are very similar to triggers, and that's because Database Webhooks are just a convenience wrapper around triggers using the [pg_net](/docs/guides/database/extensions/pgnet) extension. This extension is asynchronous, and therefore will not block your database changes for long-running network requests.
Database Webhooks are very similar to triggers, and that's because Database Webhooks are a convenience wrapper around triggers using the [pg_net](/docs/guides/database/extensions/pgnet) extension. This extension is asynchronous, and therefore will not block your database changes for long-running network requests.
This video demonstrates how you can create a new customer in Stripe each time a row is inserted into a `profiles` table:
@@ -32,7 +32,7 @@ This video demonstrates how you can create a new customer in Stripe each time a
1. Select the table you want to hook into.
1. Select one or more events (table inserts, updates, or deletes) you want to hook into.
Since webhooks are just database triggers, you can also create one from SQL statement directly.
Since webhooks are database triggers, you can also create one from SQL statement directly.
```sql
create trigger "my_webhook" after insert
@@ -475,13 +475,13 @@ This creates a new migration file capturing the current remote schema. Commit it
### Step 3: If the migration history table is wrong
If a migration shows as missing in the remote history table but the schema change is actually already there (for example, it was applied manually), you can mark it as applied without re-running it:
If a migration shows as missing in the remote history table but the schema change is already there (for example, it was applied manually), you can mark it as applied without re-running it:
```bash name=Terminal
supabase migration repair --status applied <migration-timestamp>
```
Or if a migration is recorded as applied but was never actually run:
Or if a migration is recorded as applied but was never run:
```bash name=Terminal
supabase migration repair --status reverted <migration-timestamp>
@@ -9,7 +9,7 @@ Edge Function instances can process background tasks outside of the request hand
This allows you to:
- Respond quickly to users while processing continues
- Respond to users while processing continues
- Handle async operations without blocking the response
---
@@ -40,7 +40,7 @@ If you haven't yet created a Supabase project, you can do so by visiting [databa
</Admonition>
[Link](/docs/reference/cli/usage#supabase-link) your local project to your remote Supabase project using the ID you just retrieved:
[Link](/docs/reference/cli/usage#supabase-link) your local project to your remote Supabase project using the ID you retrieved:
```bash
supabase link --project-ref your-project-id
@@ -136,4 +136,4 @@ export default {
}
```
You now have AI powered semantic search set up without any external dependencies! Just you, pgvector, and Supabase Edge Functions!
You now have AI powered semantic search set up without any external dependencies! All you need: you, pgvector, and Supabase Edge Functions!
@@ -74,7 +74,7 @@ hideToc: true
<StepHikeCompact.Details title="Initialize the Supabase client">
You can create a Supabase client whenever you need to perform an API call.
For the sake of simplicity, we will create a client in the `MainActivity.kt` file at the top just below the imports.
For the sake of simplicity, we will create a client in the `MainActivity.kt` file at the top below the imports.
Replace the `supabaseUrl` and `supabaseKey` with your own:
@@ -178,7 +178,7 @@ hideToc: true
<StepHikeCompact.Step step={9}>
<StepHikeCompact.Details title="Seed your database">
Run the seed database command to populate the `Instrument` table with the instruments you just created.
Run the seed database command to populate the `Instrument` table with the instruments you created.
<Admonition type="tip">
@@ -43,7 +43,7 @@ Run `flutter pub get` to install the dependencies.
With dependencies installed, set up deep links.
Setting up deep links is required to bring back the user to the app when they click on the magic link to sign in.
We can setup deep links with just a minor tweak on our Flutter application.
We can setup deep links with a minor tweak on our Flutter application.
We have to use `io.supabase.flutterquickstart` as the scheme. In this example, we will use `login-callback` as the host for our deep link, but you can change it to whatever you would like.
@@ -53,7 +53,7 @@ You can find the full contents of this file [in the example repository](https://
Next.js is a versatile framework offering pre-rendering at build time (SSG), server-side rendering at request time (SSR), API routes, and proxy edge-functions.
To better integrate with the framework, we've created the `@supabase/ssr` package for Server-Side Auth. It has all the functionalities to quickly configure your Supabase project to use cookies for storing user sessions. Read the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/creating-a-client?queryGroups=package-manager&package-manager=npm&queryGroups=framework&framework=nextjs) for more information.
To better integrate with the framework, we've created the `@supabase/ssr` package for Server-Side Auth. It has all the functionalities to configure your Supabase project to use cookies for storing user sessions. Read the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/creating-a-client?queryGroups=package-manager&package-manager=npm&queryGroups=framework&framework=nextjs) for more information.
Install the package for Next.js.
@@ -216,7 +216,7 @@ lines={[[1, 4], [7, 78], [88, 89], [99, -1]]}
meta="name=app/account/account-form.tsx"
/>
Create an account page for the `AccountForm` component you just created
Create an account page for the `AccountForm` component you created
<$CodeSample
path="/user-management/nextjs-user-management/app/account/page.tsx"
@@ -115,7 +115,7 @@ SUPABASE_JWT_SECRET=YOUR_SUPABASE_JWT_SECRET
</$CodeTabs>
And finally, you will also need to save **just** the `web side` environment variables to the `redwood.toml`.
And finally, you will also need to save **only** the `web side` environment variables to the `redwood.toml`.
<$CodeTabs>
@@ -207,7 +207,7 @@ The `/` is important here as it creates a root level route.
</Admonition>
You can stop the `dev` server if you want; to see your changes, just be sure to run `yarn rw dev` again.
You can stop the `dev` server if you want; to see your changes, run `yarn rw dev` again.
You should see the `Home` page route in `web/src/Routes.js`:
@@ -8,7 +8,7 @@ description: 'Manage your Supabase projects directly through Vercel'
The Vercel Marketplace is a feature that allows you to manage third-party resources, such as Supabase, directly from the Vercel platform. This integration offers a seamless experience with unified billing, streamlined authentication, and easy access management for your team.
When you create an organization and projects through Vercel Marketplace, they function just like those created directly within Supabase. However, the billing is handled through your Vercel account, and you can manage your resources directly from the Vercel dashboard or CLI. Additionally, environment variables are automatically synchronized, making them immediately available for your connected projects.
When you create an organization and projects through Vercel Marketplace, they function like those created directly within Supabase. However, the billing is handled through your Vercel account, and you can manage your resources directly from the Vercel dashboard or CLI. Additionally, environment variables are automatically synchronized, making them immediately available for your connected projects.
For more information, see [Introducing the Vercel Marketplace](https://vercel.com/blog/introducing-the-vercel-marketplace) blog post.
@@ -4,7 +4,7 @@ description: 'The Supabase CLI provides tools to develop your project locally, d
subtitle: 'Develop locally, deploy to the Supabase Platform, and set up CI/CD workflows'
---
The Supabase CLI enables you to run the entire Supabase stack locally, on your machine or in a CI environment. With just two commands, you can set up and start a new local project:
The Supabase CLI enables you to run the entire Supabase stack locally, on your machine or in a CI environment. With two commands, you can set up and start a new local project:
1. `supabase init` to create a new local project
2. `supabase start` to launch the Supabase services
@@ -7,7 +7,7 @@ video: 'https://www.youtube-nocookie.com/v/vyHyYpvjaks'
tocVideo: 'vyHyYpvjaks'
---
Supabase is a flexible platform that lets you decide how you want to build your projects. You can use the Dashboard directly to get up and running quickly, or use a proper local setup. We suggest you work locally and deploy your changes to a linked project on the [Supabase Platform](https://app.supabase.io/).
Supabase is a flexible platform that lets you decide how you want to build your projects. You can use the Dashboard directly to get up and running, or use a proper local setup. We suggest you work locally and deploy your changes to a linked project on the [Supabase Platform](https://app.supabase.io/).
Develop locally using the CLI to run a local Supabase stack. You can use the integrated Studio Dashboard to make changes, then capture your changes in schema migration files, which can be saved in version control.
@@ -53,7 +53,7 @@ You need to add a CNAME record to your domain's DNS settings to ensure your cust
If your project's default domain is `abcdefghijklmnopqrst.supabase.co` you should:
- Create a CNAME record for `api.example.com` that resolves to `abcdefghijklmnopqrst.supabase.co.`.
- Use a low TTL value to quickly propagate changes in case you make a mistake.
- Use a low TTL value to propagate changes in case you make a mistake.
### Verify ownership of the domain
@@ -73,7 +73,7 @@ Required outstanding validation records:
_acme-challenge.api.example.com. TXT -> ca3-F1HvR9i938OgVwpCFwi1jTsbhe1hvT0Ic3efPY3Q
```
Add the record to your domains' DNS settings. Make sure to trim surrounding whitespace. Use a low TTL value so you can quickly change the records if you make a mistake.
Add the record to your domains' DNS settings. Make sure to trim surrounding whitespace. Use a low TTL value so you can change the records if you make a mistake.
Some DNS registrars automatically append your domain name to the DNS entries being created. As such, creating a DNS record for `api.example.com` might instead create a record for `api.example.com.example.com`. In such cases, remove the domain name from the records you're creating; as an example, you would create a TXT record for `api`, instead of `api.example.com`.
@@ -5,7 +5,7 @@ title: 'Manage Branching usage'
## What you are charged for
Each [Preview branch](/docs/guides/deployment/branching) is a separate environment with all Supabase services (Database, Auth, Storage, etc.). You're charged for usage within that environment—such as [Compute](/docs/guides/platform/manage-your-usage/compute), [Disk Size](/docs/guides/platform/manage-your-usage/disk-size), [Egress](/docs/guides/platform/manage-your-usage/egress), and [Storage](/docs/guides/platform/manage-your-usage/storage-size)—just like the project you branched from.
Each [Preview branch](/docs/guides/deployment/branching) is a separate environment with all Supabase services (Database, Auth, Storage, etc.). You're charged for usage within that environment—such as [Compute](/docs/guides/platform/manage-your-usage/compute), [Disk Size](/docs/guides/platform/manage-your-usage/disk-size), [Egress](/docs/guides/platform/manage-your-usage/egress), and [Storage](/docs/guides/platform/manage-your-usage/storage-size)—the same as the project you branched from.
<Admonition type="note">
@@ -117,4 +117,4 @@ If you remove the IPv4 add-on, you are no longer billed from the time of removal
## Optimize usage
To see whether your database actually needs a dedicated IPv4 address, refer to [When you need the IPv4 add-on](/docs/guides/platform/ipv4-address#when-you-need-the-ipv4-add-on).
To see whether your database needs a dedicated IPv4 address, refer to [When you need the IPv4 add-on](/docs/guides/platform/ipv4-address#when-you-need-the-ipv4-add-on).
@@ -65,7 +65,7 @@ Every service in your Supabase project automatically generates Logs — you don'
- **Reduce log-level verbosity** in your Edge Functions and server-side code (for example, `info` → `warn` in production).
- **Audit verbose logging in your application code.** Application-level logs forwarded to Supabase services count toward ingest.
- **Cap log payload size.** Large structured payloads inflate GB-billed volume quickly.
- **Cap log payload size.** Large structured payloads can inflate GB-billed volume.
- **Investigate spikes.** Use the [**Logs Explorer**](/dashboard/project/_/logs-explorer) section of the Dashboard to find services or endpoints producing unusually high volume.
## Exceeding Quotas
@@ -124,7 +124,7 @@ module.exports = (collectionName, doc, recordCounters, writeRecord) => {
- `writeRecord`: This function automatically handles the process of writing data to other JSON files (useful for "flatting" your document into separate JSON files to be written to separate database tables). `writeRecord` takes the following parameters:
- `name`: Name of the JSON file to write to.
- `doc`: The document to write to the file.
- `recordCounters`: The same `recordCounters` object that was passed to this hook (just passes it on).
- `recordCounters`: The same `recordCounters` object that was passed to this hook (passes it on).
### Examples
@@ -8,7 +8,7 @@ tocVideo: 'xsRhPMphtZ4'
Supabase is one of the best [free alternatives to Heroku Postgres](/alternatives/supabase-vs-heroku-postgres). This guide shows how to migrate your Heroku Postgres database to Supabase. This migration requires the [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html) and [psql](https://www.postgresql.org/docs/current/app-psql.html) CLI tools, which are installed automatically as part of the complete Postgres installation package.
Alternatively, use the [Heroku to Supabase migration tool](https://migrate.supabase.com/) to migrate in just a few clicks.
Alternatively, use the [Heroku to Supabase migration tool](https://migrate.supabase.com/) to migrate in a few clicks.
## Quick demo
@@ -31,7 +31,7 @@ In such a scenario, you can consider:
You can use the [pg_stat_activity](https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW) view to debug which clients are holding open connections on your DB. `pg_stat_activity` only exposes information on direct connections to the database. Information on the number of connections to Supavisor is available [via the metrics endpoint](../telemetry/metrics).
Depending on the clients involved, you might be able to configure them to work with fewer connections (e.g. by imposing a limit on the maximum number of connections they're allowed to use), or shift specific workloads to connect via [Supavisor](/docs/guides/database/connecting-to-postgres#connection-pooler) instead. Transient workflows, which can quickly scale up and down in response to traffic (e.g. serverless functions), can especially benefit from using a connection pooler rather than connecting to the DB directly.
Depending on the clients involved, you might be able to configure them to work with fewer connections (e.g. by imposing a limit on the maximum number of connections they're allowed to use), or shift specific workloads to connect via [Supavisor](/docs/guides/database/connecting-to-postgres#connection-pooler) instead. Transient workflows, which can scale up and down rapidly in response to traffic (e.g. serverless functions), can especially benefit from using a connection pooler rather than connecting to the DB directly.
### Allowing higher number of connections
@@ -26,7 +26,7 @@ Supabase PrivateLink is an organisation level configuration. It works by sharing
The connection architecture changes from public internet routing to a dedicated private path through AWS's secure network backbone.
Supabase PrivateLink is currently just for direct database and PgBouncer connections only. It does not support other Supabase services like API, Storage, Auth, or Realtime. These services will continue to operate over public internet connections.
Supabase PrivateLink currently supports direct database and PgBouncer connections only. It does not support other Supabase services like API, Storage, Auth, or Realtime. These services will continue to operate over public internet connections.
## Requirements
@@ -43,7 +43,7 @@ You can only read data from a Read Replica. This is in contrast to a Primary dat
id="rr-flow"
>
When your database starts slowing down, you face a choice: make your existing database bigger (scale vertically), or spread the load across multiple databases (scale horizontally). Both approaches work. Neither is universally correct. The right answer depends on your workload, your budget, and where the bottleneck actually is.
When your database starts slowing down, you face a choice: make your existing database bigger (scale vertically), or spread the load across multiple databases (scale horizontally). Both approaches work. Neither is universally correct. The right answer depends on your workload, your budget, and where the bottleneck is.
```mermaid
flowchart TD
+1 -1
View File
@@ -74,7 +74,7 @@ Users start their login at supabase.com by entering their email address, then ar
- **Login flows** - Choose between IdP-initiated (users start from identity provider), SP-initiated (users start at supabase.com), or both. IdP-initiated is recommended for most organizations and requires no domain configuration. See [Choosing the Right Login Flow](/docs/guides/platform/sso/choosing-login-flow) for guidance.
- **Email domains** - Required only if you enable SP-initiated login. You can associate one or more email domains with your SSO provider. Users with matching email addresses can sign in via SSO at supabase.com. Not required for IdP-initiated flow.
- **Auto-join** - Optionally allow users with a matching domain to be added to your organization automatically when they sign in via SSO. Auto-join applies on every login, not just first signup, making it easy to test before enabling.
- **Auto-join** - Optionally allow users with a matching domain to join your organization automatically when they sign in via SSO. This applies on every login, not only on first signup.
- **Default role for auto-joined users** - Choose the role (e.g., `Read-only`, `Developer`, `Administrator`, `Owner`) that automatically joined users receive. We recommend using `Developer` as the default (principle of least privilege) and promoting users individually as needed. Refer to [access control](/docs/guides/platform/access-control) for more information about roles.
- **Invitation types** - When inviting users to your organization, you can explicitly choose whether the invitation requires SSO authentication or allows non-SSO login (password/social). This enables mixed authentication organizations with both SSO and non-SSO users.
@@ -64,7 +64,7 @@ First you need to download Supabase's SAML metadata file. Click the button below
Alternatively, visit this page to initiate a download: `https://alt.supabase.io/auth/v1/sso/saml/metadata?download=true`
Click on the _Upload metadata file_ option in the toolbar and select the file you just downloaded.
Click on the _Upload metadata file_ option in the toolbar and select the file you downloaded.
![Azure AD console: Supabase application, SAML-based Sign-on screen, selected Upload metadata file button](/docs/img/sso-azure-step-06-1.png)
@@ -134,7 +134,7 @@ By default this setting is disabled, users logging in via SSO will not be added
![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png)
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature.
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not only on first signup - this makes it safe to test SSO before enabling this feature.
![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png)
@@ -144,7 +144,7 @@ By default this setting is disabled, users logging in via SSO will not be added
![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png)
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature.
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not only on first signup - this makes it safe to test SSO before enabling this feature.
![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png)
@@ -130,7 +130,7 @@ By default this setting is disabled, users logging in via SSO will not be added
![Auto-join disabled](/docs/img/sso-dashboard-configure-autojoin-disabled.png)
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not just first signup - this makes it safe to test SSO before enabling this feature.
Toggle this on if you want SSO-authenticated users to be **automatically added to your organization** when they log in via SSO. Auto-join applies on **every login**, not only on first signup - this makes it safe to test SSO before enabling this feature.
![Auto-join enable](/docs/img/sso-dashboard-configure-autojoin-enabled.png)
@@ -180,7 +180,7 @@ Test with 2-3 additional users to verify:
<Admonition type="caution">
**Recent improvement:** Auto-join now applies on EVERY login, not just first signup. This resolves a common issue where org owners would test with auto-join disabled, enable it, then log in again expecting to auto-join.
**Recent improvement:** Auto-join now applies on EVERY login, not only on first signup. This resolves a common issue where org owners would test with auto-join disabled, enable it, then log in again expecting to auto-join.
</Admonition>
@@ -253,7 +253,7 @@ Test with 2-3 additional users to verify:
- Auto-join works when enabled
- Users receive correct default role
- Non-matching domains are excluded (if using SP-initiated with domains)
- Existing users auto-join on their next login (not just new signups)
- Existing users auto-join on their next login (not only on new signups)
- Auto-join can be disabled and re-enabled as needed
- Auto-join is idempotent (no duplicate memberships)
- Auto-join works with IdP-initiated only (no domains)
@@ -369,7 +369,7 @@ SSO accounts have specific restrictions to prevent accidental organization locko
- SSO accounts CAN disable SSO providers
- Non-SSO owners CAN delete SSO providers
- Error messages clearly explain the restriction
- Restriction applies to all SSO accounts (not just certain roles)
- Restriction applies to all SSO accounts (not only certain roles)
## Common issues and troubleshooting
@@ -387,7 +387,7 @@ Based on customer pain points that previously required support intervention:
**Solution:**
- Auto-join now applies on **every login**, not just first signup
- Auto-join now applies on **every login**, not only on first signup
- To test: Enable auto-join, log out completely, log back in via SSO
- If still not working, verify domain configuration matches user email exactly
@@ -795,7 +795,7 @@ Before rolling out SSO to your organization:
- Auto-join adds users to correct organization (if enabled)
- Auto-joined users receive correct default role
- Auto-join works on first login (not just signup)
- Auto-join works on first login (not only on signup)
- Existing users auto-join when feature enabled
- Auto-join is idempotent (no duplicate memberships)
- Auto-join works with IdP-initiated (no domains required)
+1 -1
View File
@@ -6,7 +6,7 @@ pgmq is a lightweight message queue built on Postgres.
## Features
- Lightweight - No background worker or external dependencies, just Postgres functions packaged in an extension
- Lightweight - No background worker or external dependencies, only Postgres functions packaged in an extension
- "exactly once" delivery of messages to a consumer within a visibility timeout
- API parity with AWS SQS and RSMQ
- Messages stay in the queue until explicitly removed
@@ -44,7 +44,7 @@ Broadcast lets you send a message from any connected client to a Channel. Any ot
This works globally. A client connected to a Realtime node in the United States can send a message to another client connected to a node in Singapore. Connect two clients to the same Realtime Channel and they'll all receive the same messages.
Broadcast is useful for getting messages to users in the same location very quickly. If a group of clients are connected to a node in Singapore, the message only needs to go to that Realtime node in Singapore and back down. If users are close to a Realtime node they'll get Broadcast messages in the time it takes to ping the cluster.
Broadcast is useful for getting messages to users in the same location rapidly. If a group of clients are connected to a node in Singapore, the message only needs to go to that Realtime node in Singapore and back down. If users are close to a Realtime node they'll get Broadcast messages in the time it takes to ping the cluster.
Thanks to the Realtime cluster, you (an amazing Supabase user) don't have to think about which regions your clients are connected to.
@@ -30,7 +30,7 @@ For high-frequency or fire-and-forget updates, use [Broadcast](/docs/guides/real
<Admonition type="note" title="Sync event behavior">
During a `sync` event, you may receive `join` and `leave` events simultaneously, even though no users are actually joining or leaving. This is expected behavior—Presence reconciles its local state with the server state, which can trigger these events as part of the synchronization process. This reflects state reconciliation, not real user movement.
During a `sync` event, you may receive `join` and `leave` events simultaneously, even though no users are joining or leaving. This is expected behavior—Presence reconciles its local state with the server state, which can trigger these events as part of the synchronization process. This reflects state reconciliation, not real user movement.
</Admonition>
@@ -413,7 +413,7 @@ user-event // User Event
}
```
The payload encoding is just a hint for the client to know if the payload should be treated as JSON or not.
The payload encoding is a hint for the client to know if the payload should be treated as JSON or not.
{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */}
@@ -667,7 +667,7 @@ message // User Event
}
```
The metadata field is JSON encoded. The payload encoding is just a hint for the client to know if the payload should be treated as JSON or not.
The metadata field is JSON encoded. The payload encoding is a hint for the client to know if the payload should be treated as JSON or not.
{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */}
@@ -962,7 +962,7 @@ When `ack` is `true`, the server replies on error with `response.error` (an atom
{ "status": "error", "response": { "error": "payload_size_exceeded" } }
```
Note that the JS client (`send()`) resolves to just the string `'error'` and does not expose the specific `error` atom to callers.
Note that the JS client (`send()`) resolves to the string `'error'` and does not expose the specific `error` atom to callers.
### Presence errors
@@ -157,7 +157,7 @@ height={625}
## Rate of Channel Joins
The Rate of Channel Joins report helps you monitor how quickly clients are joining Realtime channels over time. This metric is essential for understanding your application's channel subscription patterns and identifying when you're approaching your plan's channel join rate limits.
The Rate of Channel Joins report helps you monitor how fast clients are joining Realtime channels over time. This metric is essential for understanding your application's channel subscription patterns and identifying when you're approaching your plan's channel join rate limits.
The report displays the rate of channel joins per second, showing how frequently clients subscribe to channels throughout the selected time period. A channel join occurs whenever a client subscribes to a channel topic to receive real-time updates. Each client connection can join multiple channels (up to 100 per connection for most plans), and the join rate measures how many of these subscriptions happen per second across your entire project.
@@ -377,7 +377,7 @@ height={645}
The Response Speed report helps you monitor the average response time for HTTP requests to the Realtime service over time. This metric is essential for understanding API performance, identifying latency issues, and ensuring your real-time features meet performance expectations.
The report displays the average response time in milliseconds, showing how quickly the Realtime service responds to HTTP requests throughout the selected time period. This includes response times for REST API requests such as broadcast messages, WebSocket upgrade requests, and other HTTP-based interactions. Higher response times can indicate performance bottlenecks, database load issues, or network problems that may impact the real-time responsiveness of your application.
The report displays the average response time in milliseconds, showing how fast the Realtime service responds to HTTP requests throughout the selected time period. This includes response times for REST API requests such as broadcast messages, WebSocket upgrade requests, and other HTTP-based interactions. Higher response times can indicate performance bottlenecks, database load issues, or network problems that may impact the real-time responsiveness of your application.
<Image
alt="Response Speed chart"
@@ -90,7 +90,7 @@ A refresh token is a long-lived (in most cases with an indefinite lifetime) toke
## Refresh token flow
The refresh token flow is a mechanism that issues a new refresh and access token on the basis of a valid refresh token. It is used to extend authorization access for an application. An application that is being constantly used will invoke the refresh token flow just before the access token expires.
The refresh token flow is a mechanism that issues a new refresh and access token on the basis of a valid refresh token. It is used to extend authorization access for an application. An application that is being constantly used will invoke the refresh token flow before the access token expires.
## Replay attack
@@ -4,7 +4,7 @@ title: 'Securing npm installs'
description: 'Consumer-side guide to hardening your npm installs of Supabase packages against supply-chain attacks.'
---
A practical guide for anyone installing Supabase packages from npm — the JavaScript client libraries (`@supabase/supabase-js` and friends), the `supabase` CLI, or any other dependency in your tree — on defending against supply-chain attacks. Most of it applies to any npm package, not just Supabase's.
A practical guide for anyone installing Supabase packages from npm — the JavaScript client libraries (`@supabase/supabase-js` and friends), the `supabase` CLI, or any other dependency in your tree — on defending against supply-chain attacks. Most of it applies to any npm package, not only Supabase's.
<Admonition type="tip">
@@ -38,7 +38,7 @@ A committed lockfile is the floor, not the ceiling.
- Commit `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or `bun.lock`.
- In CI, install with `npm ci` / `pnpm install --frozen-lockfile` / `yarn install --immutable` / `bun install --frozen-lockfile`. These fail if `package.json` and the lockfile disagree, which is what you want.
- Caret ranges (`^1.2.3`) in `package.json` are fine **if** you also have a lockfile and the rest of the guidance below — the lockfile is what actually gets installed.
- Caret ranges (`^1.2.3`) in `package.json` are fine **if** you also have a lockfile and the rest of the guidance below — the lockfile is what gets installed.
**Pin transitive risk with overrides.** If you don't trust a particular transitive dep version, force a known-good version via:
@@ -102,7 +102,7 @@ npmPreapprovedPackages: # opt specific packages out of all package gates
- '@your-org/*'
```
Versions newer than the gate are excluded from resolution. Yarn's docs also note this guards against the npm registry's 72-hour unpublish window — a package you just installed could vanish, breaking your build, if you don't wait it out.
Versions newer than the gate are excluded from resolution. Yarn's docs also note this guards against the npm registry's 72-hour unpublish window — a package you recently installed could vanish, breaking your build, if you don't wait it out.
Two related yarn settings worth knowing about while you're in `.yarnrc.yml`:
@@ -141,7 +141,7 @@ Or per-command:
bun add @supabase/supabase-js --minimum-release-age 604800
```
Bun's age gate only affects new resolutions — existing entries in `bun.lock` are unchanged. It also runs a stability check: if multiple versions were published close together just outside your gate, Bun extends the filter to skip those (likely unstable) versions and picks an older, more mature one. Exact-version requests (`pkg@1.1.1`) respect the gate but bypass the stability extension.
Bun's age gate only affects new resolutions — existing entries in `bun.lock` are unchanged. It also runs a stability check: if multiple versions were published close together outside your gate, Bun extends the filter to skip those (likely unstable) versions and picks an older, more mature one. Exact-version requests (`pkg@1.1.1`) respect the gate but bypass the stability extension.
For Deno-based Edge Functions, see the [Edge Functions specifics](#edge-functions-specifics) section below.
@@ -230,7 +230,7 @@ Corepack (bundled with modern Node) and `pnpm/action-setup@v6+` both read this f
## Prune unused dependencies
Every dependency you don't actually need is attack surface you don't actually need. Two cheap habits:
Every dependency you don't need is attack surface you don't need. Two cheap habits:
- Periodically run `npx depcheck` (or the equivalent for your stack) and remove dependencies that aren't imported anywhere.
- Look at your direct dependencies when a CVE lands. Is the dep doing something you could do in 20 lines yourself? Some of the most-exploited packages are tiny utilities that became transitive footguns.
@@ -267,7 +267,7 @@ Talk to the Supabase Functions team if your security posture depends on a featur
## If you suspect you installed a compromised version
Move quickly. Order of operations:
Act promptly. Order of operations:
1. **Treat the install host as potentially compromised.** Anything readable by the user that ran the install — env vars, files, secrets in memory — should be assumed stolen.
2. **Rotate credentials reachable from that host**: cloud provider keys (AWS, GCP, Azure), Kubernetes / Vault tokens, GitHub tokens, npm tokens, SSH keys, and any Supabase service-role keys or anon keys that touched the box.
@@ -336,7 +336,7 @@ For transaction-mode connections:
psql 'postgres://postgres.[POOLER_TENANT_ID]:[POSTGRES_PASSWORD]@[your-domain]:6543/postgres'
```
When using `psql` with command-line parameters instead of a connection string to connect to Supavisor, the `-U` parameter should also be `postgres.[POOLER_TENANT_ID]`, and not just `postgres`.
When using `psql` with command-line parameters instead of a connection string to connect to Supavisor, the `-U` parameter should also be `postgres.[POOLER_TENANT_ID]`.
If you need to configure Postgres to be directly accessible from the Internet, read [Exposing your Postgres database](#exposing-your-postgres-database).
@@ -199,7 +199,7 @@ For more details, see:
### 400 "missing function name in request"
The request URL must include the function name after `/functions/v1/`. For example, `/functions/v1/hello` — not just `/functions/v1/`.
The request URL must include the function name after `/functions/v1/`. For example, `/functions/v1/hello`.
### 500 error on invocation
@@ -230,7 +230,7 @@ docker compose restart functions --no-deps
- Recreate the functions container after changing configuration
- Check that the variable name matches exactly (case-sensitive)
Use the following command to recreate the container, not just `restart`:
Use the following command to recreate the container:
```sh
docker compose up -d --force-recreate --no-deps functions
@@ -170,7 +170,7 @@ MFA_MAX_ENROLLED_FACTORS=5
## Troubleshooting
### OTP expires too quickly
### OTP expires too soon
The default `SMS_OTP_EXP` is 60 seconds. Increase it in `.env`:
@@ -39,7 +39,7 @@ For instance, you can use a URL like `/storage/v1/object/sign/profile-pictures/c
## Signed URLs and CDN caching
Signed URLs are the primary way to serve assets from private buckets to end users. With Smart CDN enabled, signed URL responses are cached at the CDN edge, just like any other storage request.
Signed URLs are the primary way to serve assets from private buckets to end users. With Smart CDN enabled, signed URL responses are cached at the CDN edge, like any other storage request.
Unlike public bucket URLs, each signed URL contains a unique token query parameter (`?token=...`). Smart CDN treats each unique token as a separate cache key, meaning the first request with any given signed URL results in a cache miss, and only subsequent requests using that exact same URL will receive a cache hit. Two different signed URLs for the same object, even if generated seconds apart, each maintain their own independent cache entry.
@@ -19,7 +19,7 @@ Files can be any sort of media file. This includes images, GIFs, and videos. It
### Folders
Folders are a way to organize your files (just like on your computer). There is no right or wrong way to organize your files. You can store them in whichever folder structure suits your project.
Folders are a way to organize your files (like on your computer). There is no right or wrong way to organize your files. You can store them in whichever folder structure suits your project.
### Buckets
@@ -39,7 +39,7 @@ using (
## Test the policy
To impersonate the `manager` role, you will need a valid JWT token with the `manager` role.
You can quickly create one using the `jsonwebtoken` library in Node.js.
You can create one using the `jsonwebtoken` library in Node.js.
<Admonition type="danger">
@@ -36,4 +36,4 @@ using (
);
```
The use of RLS policies is just one way to enforce access control. You can also implement access control in your server code by following the same pattern.
The use of RLS policies is one way to enforce access control. You can also implement access control in your server code by following the same pattern.
@@ -31,7 +31,7 @@ export const client = postgres(connectionString, { prepare: false })
## Node Postgres
[Just omit the "name" value in a query definition](https://node-postgres.com/features/queries#prepared-statements):
[Omit the "name" value in a query definition](https://node-postgres.com/features/queries#prepared-statements):
```ts
const query = {
@@ -123,7 +123,7 @@ const some_num = 5
some_num() // TypeError: some_num is not a function
```
This issue often appears when working with returned objects from external APIs. One may assume a response has a certain shape, but if the value is actually `null` or `undefined`, using it without checking can lead to a `TypeError`.
This issue often appears when working with returned objects from external APIs. One may assume a response has a certain shape, but if the value is `null` or `undefined`, using it without checking can lead to a `TypeError`.
```js
const data = await req.json() // returns undefined if request body is empty
@@ -277,7 +277,7 @@ if (req.method === 'OPTIONS') {
}
```
So, when encountering these errors, it is still important to check the logs or run the request outside the browser to make sure it is actually the primary factor and not a side-effect of a larger issue.
So, when encountering these errors, it is still important to check the logs or run the request outside the browser to make sure it is the primary factor and not a side-effect of a larger issue.
## Still stuck?
@@ -252,7 +252,7 @@ If you believe a portion of your function is overly aggressive, try testing loca
Common culprits:
**CPU intensive recursions**: intensive loops or recursion can quickly exhaust CPU
**CPU intensive recursions**: intensive loops or recursion can exhaust CPU
```js
// This will exhaust CPU allocation when called repeatedly
@@ -288,7 +288,7 @@ If you are performing logic to process data from Supabase Postgres, you may be a
### 4. Offload operations to an external API:
Instead of managing all operations within the function itself, there may be an external API that can execute CPU or memory intensive jobs on its behalf. One [example](/docs/guides/functions/examples/screenshots) would be using an external API for orchestrating a headless browser and then just using the edge function to manage the output of the activity instead of everything all in place.
Instead of managing all operations within the function itself, there may be an external API that can execute CPU or memory intensive jobs on its behalf. One [example](/docs/guides/functions/examples/screenshots) would be using an external API for orchestrating a headless browser and then using the edge function to manage the output of the activity instead of everything all in place.
### 5. Split operations into individual functions:
@@ -312,7 +312,7 @@ Performing edits against images or other large files can be both CPU and Memory
### AI embedding generation and inference
AI models process data into embeddings (large arrays), that they can more understand. Edge Functions are capable of managing [some small models directly](/blog/ai-inference-now-available-in-supabase-edge-functions); however, some require more processing power than what the edge function can support directly. In these cases, the solution is to manage the embeddings via an external source, such as OpenAI, Anthropic, etc. and to just use the edge function for light processing and coordination.
AI models process data into embeddings (large arrays), that they can more understand. Edge Functions are capable of managing [some small models directly](/blog/ai-inference-now-available-in-supabase-edge-functions); however, some require more processing power than what the edge function can support directly. In these cases, the solution is to manage the embeddings via an external source, such as OpenAI, Anthropic, etc. and to use the edge function for light processing and coordination.
### Web scraping
@@ -26,7 +26,7 @@ Review the output for:
- **Large dependencies:** Packages that contribute significantly to bundle size
- **Redundant imports:** Multiple packages providing similar functionality
- **Outdated versions:** Dependencies that can be updated to more efficient versions
- **Unused imports:** Dependencies imported but not actually used in your code
- **Unused imports:** Dependencies imported but not used in your code
## Optimizing NPM dependencies
@@ -39,18 +39,17 @@ Import specific submodules to minimize overhead:
```tsx
// Good: Import specific submodules
import { Sheets } from 'npm:@googleapis/sheets'
import * as googleAuth from 'npm:google-auth-library'
import { JWT } from 'npm:google-auth-library/build/src/auth/jwtclient'
// Avoid: Import entire package
import * as googleapis from 'npm:googleapis'
import * as googleAuth from 'npm:google-auth-library'
```
## Best practices
### Tree-shake aggressively
Only import what you actually use. Avoid wildcard imports (`import *`) when possible.
Only import what you use. Avoid wildcard imports (`import *`) when possible.
### Choose lightweight alternatives
@@ -80,11 +80,11 @@ These events are surfaced through logs and observability tools, allowing you to
### EarlyDrop
**What it means:** The runtime detected that the function has completed all its work and can be shut down early, before reaching any resource limits. This is actually the most common shutdown reason and typically indicates efficient function execution.
**What it means:** The runtime detected that the function has completed all its work and can be shut down early, before reaching any resource limits. This is the most common shutdown reason and typically indicates efficient function execution.
**When it happens:** Your function has finished processing the request, sent the response, and has no remaining async work (pending promises, timers, or callbacks). The runtime recognizes that the worker can be safely terminated without waiting for timeouts or other limits.
**Why this is good:** `EarlyDrop` means your function is running efficiently. It completed quickly, didn't exhaust resources, and the runtime could reclaim the worker for other requests. Most well-designed functions should end with `EarlyDrop`.
**Why this is good:** `EarlyDrop` means your function is running efficiently. It completed without exhausting resources, and the runtime could reclaim the worker for other requests. Most well-designed functions should end with `EarlyDrop`.
**What to do:**
@@ -62,6 +62,6 @@ select * from table_name where column_name = 'search_value';
[More on building index by expression](https://www.postgresql.org/docs/current/sql-createindex.html)
For some datatypes other than text that allows queries by partial inclusion (i.e. that the pair key-value is includes in a JSON or for implementing tsvector phrase search) you'd just use GIST/GIN indexes that inherently have values space much narrower that the whole to be indexed.
For some non-text datatypes that support queries by partial inclusion—such as checking whether a key-value pair exists in JSON, or implementing `tsvector` phrase search—use GiST or GIN indexes. These index types operate on a narrower value space than the full content being indexed.
[More on GIN/GiST indexes](https://www.postgresql.org/docs/15/textsearch-indexes.html)
@@ -40,7 +40,7 @@ This situation often arises in large, high-write tables (e.g., `your_table`, whi
### **Mitigating performance impact during a critical autovacuum**
Since the wraparound prevention autovacuum cannot be stopped, the best approach is to provide the database with sufficient resources to complete the operation as quickly and efficiently as possible.
Since the wraparound prevention autovacuum cannot be stopped, the best approach is to provide the database with sufficient resources to complete the operation as efficiently as possible.
1. **Upgrade your Database Compute Instance:**
- **Action:** Temporarily scale up your instance's CPU (e.g., from `m6g.4xlarge` to `m6g.8xlarge` or higher).
@@ -92,8 +92,8 @@ if __name__ == "__main__":
print(f"postgres: {ref}, supabase: {sup}, ratio: {sup/ref}")
```
3. You will see that the Supabase client takes longer to execute the same query, especially for smaller tables or queries returning just one row.
3. You will see that the Supabase client takes longer to execute the same query, especially for smaller tables or queries returning only one row.
## Expected behavior
The overhead from PostgREST shouldn't be higher than a few milliseconds at max. 60-70 ms is way too high. This is particular deceiving because one can run the query on the SQL Editor page and it reports the same time as the direct Postgres query, which is not what actually happens.
The overhead from PostgREST shouldn't be higher than a few milliseconds at max. 60-70 ms is way too high. This is particular deceiving because one can run the query on the SQL Editor page and it reports the same time as the direct Postgres query, which is not what happens.
@@ -77,7 +77,7 @@ In most cases, developers work with the default BTREE index. It is the most prac
An operator's functional equivalents, such as `IN`, `BETWEEN`, and `ANY`, are also valid.
However, just because the base requirements (relevant column, filter, and operators) are present, doesn't mean that an index will be used.
However, an index isn't guaranteed to be used, even when the base requirements (relevant column, filter, and operators) are present.
Indexes have a startup cost, so for small tables, Postgres might use a sequential scan if it believes that it will take less time. The database keeps statistics about each table that it uses to inform these choices.
@@ -141,7 +141,7 @@ select * from test1 where lower(col1) = 'value';
#### Covering indexes
Indexes contain pointers to a specific row, but you could instruct an index to actually hold a copy of a column's value for even faster retrieval. These are known as `covering` indexes. Because maintaining a copy is storage intensive, you should avoid using it for values with large data footprints.[ FULL VIDEO ON TOPIC](https://www.youtube.com/watch?v=bBu_V8CfWgM)
Indexes contain pointers to a specific row, but you could instruct an index to hold a copy of a column's value for even faster retrieval. These are known as `covering` indexes. Because maintaining a copy is storage intensive, you should avoid using it for values with large data footprints.[ FULL VIDEO ON TOPIC](https://www.youtube.com/watch?v=bBu_V8CfWgM)
```sql
CREATE INDEX a_b_idx ON x (a,b) INCLUDE (c);
@@ -149,7 +149,7 @@ CREATE INDEX a_b_idx ON x (a,b) INCLUDE (c);
#### Indexes on JSONB
Although a GIN/GIST index can be used to index entire JSONB bodies, you can also target just specific Key-values with standard BTREE indexes:
Although a GIN/GIST index can be used to index entire JSONB bodies, you can also target only specific Key-values with standard BTREE indexes:
```sql
-- Example table
@@ -71,7 +71,7 @@ This is a Grafana Chart of unhealthy memory usage:
![image](/docs/img/troubleshooting/47685206-7914-440e-a010-da62f5c38186.png)
- Yellow: represents active memory
- Red: represents SWAP, which is disk storage that the system treats as if it were actually memory
- Red: represents SWAP, which is disk storage that the system treats as if it were memory
- Green: it is unclaimed (the system will always leave some memory unclaimed)
- Blue: it is cached data and a buffer
@@ -263,7 +263,7 @@ limit 100;
When recording what is accessed and by whom, logging based on database roles and objects is the most reliable way to ensure a proper trail of activity.
You can use the [pg_audit](/docs/guides/database/extensions/pgaudit) extension to selectively log relevant queries (not just errors) by certain roles, against specific database objects.
You can use the [pg_audit](/docs/guides/database/extensions/pgaudit) extension to selectively log relevant queries, not only errors, by certain roles, against specific database objects.
You should take care when using the extension to not log all database events, but only what is absolutely necessary. Over-logging can strain the database and create log noise that makes it difficult to filter for relevant events.
@@ -28,7 +28,7 @@ SELECT nextval(pg_get_serial_sequence('<public.table_name>', '<sequenced_column_
If the values are off by more than 1, you need to resynchronize your sequence.
Back up your PG database by restarting in the [General Settings](/dashboard/project/_/settings/general) (just in case). When you restore your database, you will have a backup saved. Alternatively, you can also just download your properties table instead as a backup.
Back up your PG database by restarting in the [General Settings](/dashboard/project/_/settings/general) as a precaution. When you restore your database, you will have a backup saved. Alternatively, you can also download your properties table instead as a backup.
Then you can run this:
@@ -40,7 +40,7 @@ npx supabase secrets set --env-file ./supabase/.env --project-ref <PROJECT REF>
npx supabase secrets list
```
For security reasons, it is not advised to log secrets, but you can log a truncated version just for the reassurance that they're being updated:
For security reasons, it is not advised to log secrets, but you can log a truncated version for the reassurance that they're being updated:
```typescript
//logs the function call and the secrets
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff. Show more