Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,7 @@ export const api: NavMenuConstant = {
items: [
{ name: 'How API Keys work', url: '/guides/api/api-keys' },
{ name: 'Securing your API', url: '/guides/api/securing-your-api' },
{ name: 'Hardening the Data API', url: '/guides/api/hardening-data-api' },
{ name: 'Data API', url: '/guides/database/data-api' },
{
name: 'Custom Claims & RBAC',
url: '/guides/api/custom-claims-and-role-based-access-control-rbac',
Expand Down Expand Up @@ -2485,7 +2485,7 @@ export const security: NavMenuConstant = {
url: '/guides/deployment/shared-responsibility-model' as `/${string}`,
},
{ name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' },
{ name: 'Hardening the Data API', url: '/guides/api/hardening-data-api' },
{ name: 'Data API', url: '/guides/database/data-api' },
],
},
],
Expand Down
5 changes: 4 additions & 1 deletion apps/docs/content/_partials/quickstart_db_setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ curl -X POST https://api.supabase.com/v1/projects \

<StepHikeCompact.Details>

When your project is up and running, go to the [Table Editor](/dashboard/project/_/editor), create a new table and insert some data.
When your project is up and running, go to the [**Table Editor**](/dashboard/project/_/editor) section of the Dashboard, create a new table and insert some data. Then in the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose the specific tables or functions you want to access. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**.

Alternatively, you can run the following snippet in your project's [SQL Editor](/dashboard/project/_/sql/new). This will create an `instruments` table with some sample data.

Expand All @@ -54,6 +54,9 @@ Alternatively, you can run the following snippet in your project's [SQL Editor](
('cello');

alter table instruments enable row level security;

-- Enable read access for the Data API
grant select on public.instruments to anon;
```

</StepHikeCompact.Code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ const fetchWithRetry = fetchRetry(fetch, {
attempt < 3
&& response
&& response.status == 520 // Cloudflare errors
&& response.url.includes('rpc/your_stored_procedure')
&& response.url.includes('rpc/your_database_function')

if (shouldRetry(attempt, error, response)) {
console.log(`Retrying request... Attempt #${attempt}`, response)
Expand All @@ -119,9 +119,9 @@ const fetchWithRetry = fetchRetry(fetch, {
}
})

async function yourStoredProcedure() {
async function yourDatabaseFunction() {
const { data, error } = await supabase
.rpc('your_stored_procedure', { param1: 'value1' });
.rpc('your_database_function', { param1: 'value1' });

if (error) {
console.log('Error executing RPC:', error);
Expand All @@ -130,10 +130,10 @@ async function yourStoredProcedure() {
}
}

yourStoredProcedure();
yourDatabaseFunction();
```

By using `retryOn` with a custom function, you can define specific conditions for retrying requests. In this example, the retry logic is applied only to requests targeting a specific stored procedure.
By using `retryOn` with a custom function, you can define specific conditions for retrying requests. In this example, the retry logic is applied only to requests targeting a specific database function.

## Conclusion

Expand Down
27 changes: 19 additions & 8 deletions apps/docs/content/guides/api/creating-routes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,39 @@ This creates a corresponding route `todos` which can accept `GET`, `POST`, `PATC
1. Click **Save**.
1. Click **New Column** and create a column with the name `task` and type `text`.
1. Click **Save**.

<video width="99%" muted playsInline controls={true}>
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-create-table-sm.mp4"
type="video/mp4"
/>
</video>
1. In the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose specific tables like `todos` or the functions you want to access. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**.

</TabPanel>
<TabPanel id="sql" label="SQL">

```sql
-- Create a table called "todos" with a column to store tasks.
-- Create a table called "todos" with a column to store tasks.
create table
todos (
id bigint generated by default as identity primary key,
task text check (char_length(task) > 3)
);

-- Enable Data API access with least-privilege grants
-- Allow read-only access for anonymous clients
grant select on public.todos to anon;
-- Allow full CRUD for authenticated clients
grant select, insert, update, delete on public.todos to authenticated;
-- Allow full CRUD for the server-side service role
grant select, insert, update, delete on public.todos to service_role;
-- Important: enable Row Level Security and create appropriate policies
-- before granting write access to client roles (see RLS guide)
```

</TabPanel>
</Tabs>

<Admonition type="note" title="What it means to expose tables or functions via the API">

Granting privileges (like `select` or `execute`) to roles such as `anon` or `authenticated` makes those tables or functions accessible through the Data API. Behind the scenes, the API checks your Postgres permissions—only objects with explicit grants are exposed, and all other access is denied by default.

</Admonition>

## API URL and keys

Every Supabase project has a unique API URL. Your API is secured behind an API gateway which requires an API Key for every request.
Expand Down Expand Up @@ -92,6 +102,7 @@ using the API URL (`SUPABASE_URL`) and Key (`SUPABASE_PUBLISHABLE_KEY`) we provi
```javascript
// Initialize the JS client
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY)

// Make a request
Expand Down
137 changes: 0 additions & 137 deletions apps/docs/content/guides/api/hardening-data-api.mdx

This file was deleted.

30 changes: 23 additions & 7 deletions apps/docs/content/guides/api/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,21 @@ We'll create a database table called `todos` for storing tasks. This creates a c
id serial primary key,
task text
);

-- Enable Data API access
-- Allow read-only access for anonymous clients
grant select on public.todos to anon;
```

</TabPanel>
<TabPanel id="dashboard" label="Dashboard">

<video width="99%" muted playsInline controls={true}>
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-create-table-sm.mp4"
type="video/mp4"
/>
</video>
1. Go to the [**Table editor**](/dashboard/project/_/editor) section in the Dashboard.
2. Click **New Table** and create a table with the name `todos`.
3. Click **Save**.
4. Click **New Column** and create a column with the name `task` and type `text`.
5. Click **Save**.
6. In the [**Integrations > Data API**](/dashboard/project/_/integrations/data_api/settings) section of the Dashboard, expose the `todos` table. To automatically grant access for new tables and functions in `public`, enable **Default privileges for new entities**.

</TabPanel>
</Tabs>
Expand All @@ -61,7 +65,7 @@ We'll create a database table called `todos` for storing tasks. This creates a c

<StepHikeCompact.Details title="Allow public access">

Let's turn on Row Level Security for this table and allow public access.
Let's turn on Row Level Security for this table, create the required policies, and then grant any write access.

</StepHikeCompact.Details>

Expand All @@ -78,6 +82,18 @@ We'll create a database table called `todos` for storing tasks. This creates a c
for select
to anon
using (true);

-- Allow authenticated users to read and modify todos
create policy "Allow authenticated users to manage todos"
on todos
for all
to authenticated
using (true)
with check (true);

-- Grant write access only after RLS and policies are in place
grant select, insert, update, delete on public.todos to authenticated;
grant select, insert, update, delete on public.todos to service_role;
```

</StepHikeCompact.Code>
Expand Down
16 changes: 6 additions & 10 deletions apps/docs/content/guides/api/securing-your-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ The data APIs are designed to work with Postgres Row Level Security (RLS). If yo

To control access to your data, you can use [Policies](/docs/guides/auth#policies).

## Enabling row level security
## Add RLS policies

Any table you create in the `public` schema will be accessible via the Supabase Data API.
Enable Row Level Security (RLS) on all tables and views you have exposed via the Data API. You can then write RLS policies to grant users access to specific database rows based on their authentication token.

To restrict access, enable Row Level Security (RLS) on all tables, views, and functions in the `public` schema. You can then write RLS policies to grant users access to specific database rows or functions based on their authentication token.
For functions, RLS does not apply. Instead, control access by granting `EXECUTE` privileges only to the roles that should be able to call the function, and review any `SECURITY DEFINER` functions carefully.

<Admonition type="danger">

Always enable Row Level Security on tables, views, and functions in the `public` schema to protect your data.
Always enable Row Level Security on tables and views you expose via the Data API to protect your data. For functions, restrict access by granting `EXECUTE` only to appropriate roles.

</Admonition>

Expand Down Expand Up @@ -49,14 +49,10 @@ With RLS enabled, you can create Policies that allow or disallow users to access

<Admonition type="danger">

Any table **without RLS enabled** in the `public` schema will be accessible to the public, using the `anon` role. Always make sure that RLS is enabled or that you've got other security measures in place to avoid unauthorized access to your project's data!
Any exposed table **without RLS enabled** can be accessed by roles with matching Data API grants (for example, `anon`). Always make sure RLS is enabled, or that you've got other controls in place to avoid unauthorized access to your project's data.

</Admonition>

## Disable the API or restrict to custom schema

If you don't use the Data API, or if you don't want to expose the `public` schema, you can either disable it entirely or change the automatically exposed schema to one of your choice. See **[Hardening the Data API](/docs/guides/api/hardening-data-api)** for instructions.

## Enforce additional rules on each request

Using Row Level Security policies may not always be adequate or sufficient to protect APIs.
Expand All @@ -66,7 +62,7 @@ Here are some common situations where additional protections are necessary:
- Enforcing per-IP or per-user rate limits.
- Checking custom or additional API keys before allowing further access.
- Rejecting requests after exceeding a quota or requiring payment.
- Disallowing direct access to certain tables, views or functions in the `public` schema.
- Disallowing direct access to certain tables, views, or functions in exposed schemas.

You can build these cases in your application by creating a Postgres function that will read information from the request and perform additional checks, such as counting the number of requests received or checking that an API key is already registered in your database before serving the response.

Expand Down
Loading
Loading