{props.meta?.tocVideo && !!tocVideoPreview && (
+
{props.meta.tocVideo}
)}
diff --git a/apps/docs/pages/guides/auth/row-level-security.mdx b/apps/docs/pages/guides/auth/row-level-security.mdx
index 148c62ad2152f..2a3ba432e2488 100644
--- a/apps/docs/pages/guides/auth/row-level-security.mdx
+++ b/apps/docs/pages/guides/auth/row-level-security.mdx
@@ -3,8 +3,9 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'row-level-security',
title: 'Row Level Security',
- description: 'Secure your data using Postgres Row Level Security.',
- video: 'https://www.youtube.com/v/Ow_Uzedfohk',
+ description: 'Using Row Level Security with Supabase Auth.',
+ subtitle: 'Using Row Level Security with Supabase Auth.',
+ tocVideo: 'Ow_Uzedfohk',
}
When you need granular authorization rules, nothing beats PostgreSQL's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
@@ -55,30 +56,6 @@ Returns the JWT of the user making the request.
Here are some examples to show you the power of PostgreSQL's RLS.
-### Allow read access
-
-```sql
--- 1. Create table
-create table profiles (
- id uuid references auth.users,
- avatar_url text
-);
-
--- 2. Enable RLS
-alter table profiles
- enable row level security;
-
--- 3. Create Policy
-create policy "Public profiles are viewable by everyone."
- on profiles for select using (
- true
- );
-```
-
-1. Creates a table called `profiles` in the public schema (default schema).
-2. Enables Row Level Security.
-3. Creates a policy which allows all `select` queries to run.
-
### Restrict updates
```sql
@@ -210,31 +187,6 @@ create policy "Only Blizzard staff can update leaderboard"
);
```
-### Time to live for rows
-
-Policies can also be used to implement TTL or time to live feature that you see in Instagram stories or Snapchat.
-In the following example, rows of `stories` table are available only if they have been created within the last 24 hours.
-
-```sql
--- 1. Create table
-create table if not exists stories (
- id uuid not null primary key DEFAULT uuid_generate_v4(),
- created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
- content text not null
-);
-
--- 2. Enable RLS
-alter table stories
- enable row level security;
-
--- 3. Create Policy
-create policy "Stories are live for a day"
- on stories
- for select using (
- created_at > (current_timestamp - interval '1 day')
- );
-```
-
### Advanced policies
Use the full power of SQL to build extremely advanced rules.
@@ -285,17 +237,20 @@ using (
);
```
-## Tips
-
-### You don't have to use policies
+## Using external authorization systems
-You can also put your authorization rules in your middleware, similar to how you would create security rules with any other `backend <-> middleware <-> frontend` architecture. You can use Edge Functions to run this architecture, or you can use your favorite server framework, like Rails, Django, Node.js, Phoenix, or Laravel.
+If you want to use another authorization method for your applications that's also fine. Supabase is "just Postgres", so if your application works with Postgres, then it also works with Supabase. If you take this path, don't put your tables in the `public` schema - instead create a new schema for your tables and functions:
-Policies are a tool. In the case of "serverless/Jamstack" setups, they are especially effective because you don't have to deploy any middleware at all and can use the javascript libraries directly from the browser.
+```sql
+create schema private;
-That said, if you want to use another authorization method for your applications that's also fine. Supabase is "just Postgres", so if your application works with Postgres, then it also works with Supabase.
+create table private.employees (
+ id serial primary key,
+ name text
+);
+```
-If you plan to use this approach make sure to enable RLS for your tables. Then use the `service_role` key (for our client libraries) or the `postgres` role - both of these can bypass RLS. You don't need to create any policies with this approach, simply enabling RLS is sufficient:
+If you do put anything in the `public` schema, make sure to enable RLS (you don't need to add any policies):
```sql
create table profiles (
@@ -306,6 +261,9 @@ create table profiles (
alter table profiles enable row level security;
```
+This makes the tables inaccessible via the [Serverless APIs](/docs/guides/api).
+
+
### Never use a service key on the client
Supabase provides special "Service" keys, which can be used to bypass all RLS.
diff --git a/apps/docs/pages/guides/database/full-text-search.mdx b/apps/docs/pages/guides/database/full-text-search.mdx
index 430bddc471c74..14981b182e394 100644
--- a/apps/docs/pages/guides/database/full-text-search.mdx
+++ b/apps/docs/pages/guides/database/full-text-search.mdx
@@ -4,7 +4,8 @@ export const meta = {
id: 'full-text-search',
title: 'Full Text Search',
description: 'How to use full text search in PostgreSQL.',
- video: 'https://www.youtube.com/v/b-mgca_2Oe4',
+ subtitle: 'How to use full text search in PostgreSQL.',
+ tocVideo: 'b-mgca_2Oe4',
}
Postgres has built-in functions to handle `Full Text Search` queries. This is like a "search engine" within Postgres.
diff --git a/apps/docs/pages/guides/database/inspect.mdx b/apps/docs/pages/guides/database/inspect.mdx
index 7cf71d50fd862..5b747333c04f2 100644
--- a/apps/docs/pages/guides/database/inspect.mdx
+++ b/apps/docs/pages/guides/database/inspect.mdx
@@ -2,7 +2,7 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'json',
- title: 'Analyzing efficiency and performance from the terminal',
+ title: 'Debugging and monitoring',
description:
'Inspecting your Postgres database for common issues around disk, query performance, index, locks, and more using the terminal.',
}
diff --git a/apps/docs/pages/guides/database/managing-passwords.mdx b/apps/docs/pages/guides/database/managing-passwords.mdx
index f96c00437ac6c..4fc8c3a8b36c7 100644
--- a/apps/docs/pages/guides/database/managing-passwords.mdx
+++ b/apps/docs/pages/guides/database/managing-passwords.mdx
@@ -6,7 +6,7 @@ export const meta = {
description: 'How to change your PostgreSQL database password.',
}
-Your PostgreSQL database is the core of your Supabase project, so it's important that it has a strong, secure password at all times.
+Your Postgres database is the core of your Supabase project, so it's important that it has a strong, secure password at all times.
If you use special symbols in your postgres password, you must remember to [percent-encode](https://en.wikipedia.org/wiki/Percent-encoding) your password later if using the postgres connection string e.g. `postgresql://postgres:p%3Dword@db.cvwawazfelidkloqmbma.supabase.co:5432/postgres`
diff --git a/apps/docs/pages/guides/database/postgres/configuration.mdx b/apps/docs/pages/guides/database/postgres/configuration.mdx
new file mode 100644
index 0000000000000..77305c0e9f5dc
--- /dev/null
+++ b/apps/docs/pages/guides/database/postgres/configuration.mdx
@@ -0,0 +1,110 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'postgres-configuration',
+ title: 'Database configuration',
+ slug: 'postgres-configuration',
+ description: 'Updating the default congfiguration for your Postgres database.',
+ subtitle: 'Updating the default congfiguration for your Postgres database.',
+}
+
+Postgres provides a set of sensible defaults for you database size. In some cases, these defaults can be updated. We do not recommend changing these defaults unless you know what you're doing.
+
+## Timeouts
+
+By default, Supabase limits the maximum statement execution time to _8 seconds_ for users accessing the API. Additionally, all users are subject to a global limit of _2 minutes_. This serves as a backstop against resource exhaustion due to either poorly written queries, or abusive usage.
+
+### Changing the default timeout
+
+The timeout values were picked as a reasonable default for the majority of use-cases, but can be modified using the [`alter role`](https://www.postgresql.org/docs/current/sql-alterrole.html) statement:
+
+```sql
+-- For API users
+alter role authenticator set statement_timeout = '15s';
+```
+
+You can also update the statement timeout for a session:
+
+```sql
+set statement_timeout to 60000; -- 1 minute in milliseconds
+```
+
+### Statement Optimization
+
+All Supabase projects come with the [`pg_stat_statements`](https://www.postgresql.org/docs/current/pgstatstatements.html) extension installed, which tracks planning and execution statistics for all statements executed against it. These statistics can be used in order to diagnose the performance of your project.
+
+This data can further be used in conjunction with the [`explain`](https://www.postgresql.org/docs/current/using-explain.html) functionality of Postgres to optimize your usage.
+
+## Managing Timezones
+
+Every Supabase database is set to UTC timezone by default. We strongly recommend keeping it this way, even if your users are in a different location.
+This is because it makes it much easier to calculate differences between timezones if you adopt the mental model that "everything in my database is in UTC time".
+
+### Change timezone
+
+
+
+
+```sql
+alter database postgres
+set timezone to 'America/New_York';
+```
+
+
+
+
+### Full list of timezones
+
+Get a full list of timezones supported by your database. This will return the following columns:
+
+- `name`: Time zone name
+- `abbrev`: Time zone abbreviation
+- `utc_offset`: Offset from UTC (positive means east of Greenwich)
+- `is_dst`: True if currently observing daylight savings
+
+
+
+
+```sql
+select name, abbrev, utc_offset, is_dst
+from pg_timezone_names()
+order by name;
+```
+
+
+
+
+### Search for a specific timezone
+
+Use `ilike` (case insensitive search) to find specific timezones.
+
+
+
+
+```sql
+select *
+from pg_timezone_names()
+where name ilike '%york%';
+```
+
+
+
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/postgres-roles.mdx b/apps/docs/pages/guides/database/postgres/roles.mdx
similarity index 72%
rename from apps/docs/pages/guides/database/postgres-roles.mdx
rename to apps/docs/pages/guides/database/postgres/roles.mdx
index b2e68cc71651c..c8c4af2395c16 100644
--- a/apps/docs/pages/guides/database/postgres-roles.mdx
+++ b/apps/docs/pages/guides/database/postgres/roles.mdx
@@ -13,11 +13,11 @@ PostgreSQL manages database access permissions using the concept of roles. Gener
In PostgreSQL, roles can function as users or groups of users. Users are roles with login privileges, while groups (also known as role groups) are roles that don't have login privileges but can be used to manage permissions for multiple users.
-## Superuser Roles
+## Superuser roles
The superuser role has unrestricted access to the database system. Typically these roles will be able to bypass all security measures (like Row Level Security). Be cautious when granting superuser privileges as it can potentially lead to security risks.
-## Creating Roles
+## Creating roles
You can create a role using the `create role` command:
@@ -25,6 +25,31 @@ You can create a role using the `create role` command:
create role "role_name";
```
+## Creating users
+
+Roles and users are essentially the same in Postgres, however if you want to use password-logins for a specific role, then you can use `WITH LOGIN PASSWORD`:
+
+```sql
+create role "role_name" with login 'extremely_secure_password';
+```
+
+## Passwords
+
+Your Postgres database is the core of your Supabase project, so it's important that every role has a strong, secure password at all times. Here are some tips for creating a secure password:
+
+- Use a password manager to generate it.
+- Make a long password (12 characters at least).
+- Don't use any common dictionary words.
+- Use both upper and lower case characters, numbers, and special symbols.
+
+### Special symbols in passwords
+
+If you use special symbols in your Postgres password, you must remember to [percent-encode](https://en.wikipedia.org/wiki/Percent-encoding) your password later if using the Postgres connection string e.g. `postgresql://postgres:p%3Dword@db.xxx.supabase.co:5432/postgres`.
+
+## Changing your project password
+
+When you created your project you were also asked to enter a password. This is actually the password for the `postgres` role in your database. You can update this from the Dashboard under the [database settings](https://supabase.com/dashboard/project/_/settings/database) page. You should _never_ give this to 3rd-party service unless you absolutely trust them. Instead, we recommend that you create a new user for every service that you want to give access too. This will also help you with debugging - you can see every query that each role is executing in your database within `pg_stat_statements`.
+
## Granting Permissions
Roles can be granted various permissions on database objects using the `GRANT` command. Permissions include `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. You can configure access to almost any object inside your database - including tables, views, functions, and triggers.
diff --git a/apps/docs/pages/guides/database/postgres/row-level-security.mdx b/apps/docs/pages/guides/database/postgres/row-level-security.mdx
new file mode 100644
index 0000000000000..56cb2c5478cbc
--- /dev/null
+++ b/apps/docs/pages/guides/database/postgres/row-level-security.mdx
@@ -0,0 +1,127 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'row-level-security',
+ title: 'Row Level Security',
+ description: 'Secure your data using Postgres Row Level Security.',
+ subtitle: 'Secure your data using Postgres Row Level Security.',
+}
+
+When you need granular authorization rules, nothing beats Postgres's [Row Level Security (RLS)](https://www.postgresql.org/docs/current/ddl-rowsecurity.html).
+
+## Row Level Security in Supabase
+
+RLS is incredibly powerful and flexible, allowing you to write complex SQL rules that fit your unique business needs. RLS can be [combined with Supabase Auth](/docs/guides/auth/row-level-security) for end-to-end user security from the browser to the database.
+
+Alternatively, RLS can provide "[defence in depth](
)" to protect your data from malicious actors if you're using your own tooling with the Postgres database we provide.
+
+## Policies
+
+[Policies](https://www.postgresql.org/docs/current/sql-createpolicy.html) are Postgres's rule engine. Policies are easy to understand once you get the hang of them. Each policy is attached to a table, and the policy is executed every time a table is accessed.
+
+You can just think of them as adding a `WHERE` clause to every query. For example a policy like this ...
+
+```sql
+create policy "Individuals can view their own todos."
+on todos for select
+using ( auth.uid() = user_id );
+```
+
+.. would translate to this whenever a user tries to select from the todos table:
+
+```sql
+select *
+from todos
+where auth.uid() = todos.user_id;
+-- Policy is implicitly added.
+```
+
+## Enabling Row Level Security
+
+You can enable RLS for any table using the `enable row level security` command:
+
+```sql
+alter table "table_name" enable row level security;
+```
+
+Once you have enabled RLS, no data will be accessible until you create policies.
+
+## Creating Policies
+
+Policies are simply SQL logic that you attach to a Postgres table. You can attach as many policies as you want to each table.
+
+Supabase provides some [helpers](/docs/guides/auth/row-level-security#helper-functions) that simplify RLS if you're using Supabase Auth. We'll use these helpers to illustrate some basic Policies:
+
+### SELECT Policies
+
+You can specify select policies with the `using` command.
+
+Let's say you have a table called `profiles` in the public schema and you want enable read access to everyone.
+
+```sql
+-- 1. Create table
+create table profiles (
+ id uuid primary key,
+ user_id references auth.users,
+ avatar_url text
+);
+
+-- 2. Enable RLS
+alter table profiles enable row level security;
+
+-- 3. Create Policy
+create policy "Public profiles are visible to everyone."
+on profiles for select
+to anon -- the Postgres Role (recommended)
+using ( true ); -- the actual Policy
+```
+
+Alternatively if you only wanted users to be able to see their own profiles:
+
+```sql
+create policy "Profiles are viewable by everyone."
+on profiles
+for select using ( auth.uid() = user_id );
+```
+
+### INSERT Policies
+
+You can specify select policies with the `with check` command.
+
+Let's say you have a table called `profiles` in the public schema and you only want users to be able to create a profile for themselves. In that case, we want to check their User ID matches the value that they are trying to insert:
+
+```sql
+-- 1. Create table
+create table profiles (
+ id uuid primary key,
+ user_id references auth.users,
+ avatar_url text
+);
+
+-- 2. Enable RLS
+alter table profiles enable row level security;
+
+-- 3. Create Policy
+create policy "Users can create a profile."
+on profiles for insert
+to authenticated -- the Postgres Role (recommended)
+using ( auth.id() = user_id ); -- the actual Policy
+```
+
+## Bypassing Row Level Security
+
+You can create [Postgres Roles](/docs/guides/database/postgres/roles) which can bypass Row Level Security using the "bypass RLS" privelege:
+
+```sql
+grant bypassrls on "table_name" to "role_name";
+```
+
+This can be useful for system-level access. You should _never_ share login credentials for any Postgres Role with this privelege.
+
+## RLS Performance
+
+TBD
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/postgres/triggers.mdx b/apps/docs/pages/guides/database/postgres/triggers.mdx
new file mode 100644
index 0000000000000..f6c319280a2f4
--- /dev/null
+++ b/apps/docs/pages/guides/database/postgres/triggers.mdx
@@ -0,0 +1,122 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+
+export const meta = {
+ id: 'postgres-triggers',
+ title: 'Postgres Triggers',
+ description: 'Automatically execute SQL on table events.',
+ subtitle: 'Automatically execute SQL on table events.',
+}
+
+In Postgres, a trigger executes a set of actions automatically on table events such as INSERTs, UPDATEs, DELETEs, or TRUNCATE operations.
+
+## Creating a Trigger
+
+Creating triggers involve 2 parts:
+
+1. A [Function](docs/guides/database/functions) which will be executed (called the Trigger Function)
+2. The actualTtrigger object, with parameters around when the trigger should be run.
+
+An example of a trigger is:
+
+```sql
+create trigger "trigger_name"
+after insert on "table_name"
+for each row
+execute statement trigger_function();
+```
+
+## Trigger Functions
+
+A trigger function is a user-defined [Function](docs/guides/database/functions) that Postgres executes when the trigger is fired.
+
+### Example trigger function
+
+Here is an example that updates `salary_log` whenever an employee's salary is updated:
+
+```sql
+-- Example: Update salary_log when salary is updated
+create function update_salary_log()
+returns trigger
+language plpgsql
+as $$
+begin
+ insert into salary_log(employee_id, old_salary, new_salary)
+ values (new.id, old.salary, new.salary);
+ return new;
+end;
+$$;
+
+create trigger salary_update_trigger
+after update on employees
+for each row
+exectute function update_salary_log();
+```
+
+### Trigger variables
+
+Trigger functions have access to several special variables that provide information about the context of the trigger event and the data being modified. In the example above you can see the values inserted into the salary log are `old.salary` and `new.salary` - in this case `old` specifies the previous values and `new` specifies the updated values.
+
+Here are some of the key variables and options available within trigger functions:
+
+- `TG_NAME`: The name of the trigger being fired.
+- `TG_WHEN`: The timing of the trigger event (`BEFORE` or `AFTER`).
+- `TG_OP`: The operation that triggered the event (`INSERT`, `UPDATE`, `DELETE`, or `TRUNCATE`).
+- `OLD`: A record variable holding the old row's data in `UPDATE` and `DELETE` triggers.
+- `NEW`: A record variable holding the new row's data in `UPDATE` and `INSERT` triggers.
+- `TG_LEVEL`: The trigger level (`ROW` or `STATEMENT`), indicating whether the trigger is row-level or statement-level.
+- `TG_RELID`: The object ID of the table on which the trigger is being fired.
+- `TG_TABLE_NAME`: The name of the table on which the trigger is being fired.
+- `TG_TABLE_SCHEMA`: The schema of the table on which the trigger is being fired.
+- `TG_ARGV`: An array of string arguments provided when creating the trigger.
+- `TG_NARGS`: The number of arguments in the `TG_ARGV` array.
+
+## Types of Triggers
+
+There are two types of trigger, `BEFORE` and `AFTER`:
+
+### Trigger before changes are made
+
+Executes before the triggering event.
+
+```sql
+create trigger before_insert_trigger
+before insert on orders
+for each row
+execute statement before_insert_function();
+```
+
+### Trigger after changed are made
+
+Executes after the triggering event.
+
+```sql
+create trigger after_delete_trigger
+after delete on customers
+for each row
+execute statement after_delete_function();
+```
+
+## Execution frequency
+
+There are two options available for executing triggers:
+
+- `for each row`: specifies that the trigger function should be executed once for each affected row.
+- `for each statement`: the trigger is executed once for the entire operation (eg, once on insert). This can be more efficient than `for each row` when dealing with multiple rows affected by a single SQL statement, as they allow you to perform calculations or updates on groups of rows at once.
+
+## Dropping a Trigger
+
+You can delete a trigger using the `drop trigger` command:
+
+```sql
+drop trigger "trigger_name" on "table_name";
+```
+
+## Resources
+
+- Official Postgres Docs: [Triggers](https://www.postgresql.org/docs/current/triggers.html)
+- Official Postgres Docs: [Overview of Trigger Behavior](https://www.postgresql.org/docs/current/trigger-definition.html)
+- Official Postgres Docs: [CREATE TRIGGER](https://www.postgresql.org/docs/current/sql-createtrigger.html)
+
+export const Page = ({ children }) =>
+
+export default Page
diff --git a/apps/docs/pages/guides/database/query-optimization.mdx b/apps/docs/pages/guides/database/query-optimization.mdx
index 3176c49adafe0..7dc76544c26f6 100644
--- a/apps/docs/pages/guides/database/query-optimization.mdx
+++ b/apps/docs/pages/guides/database/query-optimization.mdx
@@ -2,12 +2,12 @@ import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
title: 'Query Optimization',
- description: 'Choosing indexes',
+ description: 'Choosing indexes to improve your query performance.',
+ subtitle: 'Choosing indexes to improve your query performance.',
footerHelpType: 'postgres',
}
-When working with PostgreSQL, or any relational database, indexing is key to improving query performance.
-Aligning indexes with common query patterns can speed up data retrieval by an order of magnitude.
+When working with Postgres, or any relational database, indexing is key to improving query performance. Aligning indexes with common query patterns can speed up data retrieval by an order of magnitude.
This guide is intended to:
@@ -90,7 +90,7 @@ explain select * from customers where sign_up_date > 25;
### Use Appropriate Index Types
-PostgreSQL offers various index types like [B-tree, Hash, GIN, etc](https://www.postgresql.org/docs/current/indexes-types.html). Select the type that best suits your data and query pattern. Using the right index type can make a significant difference. For example, using a BRIN index on a field that always increases and lives within a table that updates infrequently - like `created_at` on an `orders` table - routinely results in indexes that are +10x smaller than the equivalent default B-tree index. That translates into better scalability.
+Postgres offers various index types like [B-tree, Hash, GIN, etc](https://www.Postgres.org/docs/current/indexes-types.html). Select the type that best suits your data and query pattern. Using the right index type can make a significant difference. For example, using a BRIN index on a field that always increases and lives within a table that updates infrequently - like `created_at` on an `orders` table - routinely results in indexes that are +10x smaller than the equivalent default B-tree index. That translates into better scalability.
```sql
create index idx_orders_created_at ON customers using brin(created_at);
@@ -127,7 +127,7 @@ analyze customers;
---
-By following this guide, you'll be able to discern where indexes can optimize queries and enhance your PostgreSQL performance. Remember that each database is unique, so always consider the specific context and use case of your queries.
+By following this guide, you'll be able to discern where indexes can optimize queries and enhance your Postgres performance. Remember that each database is unique, so always consider the specific context and use case of your queries.
export const Page = ({ children }) =>
diff --git a/apps/docs/pages/guides/database/timeouts.mdx b/apps/docs/pages/guides/database/timeouts.mdx
deleted file mode 100644
index 78bb850836f28..0000000000000
--- a/apps/docs/pages/guides/database/timeouts.mdx
+++ /dev/null
@@ -1,34 +0,0 @@
-import Layout from '~/layouts/DefaultGuideLayout'
-
-export const meta = {
- id: 'timeouts',
- title: 'Timeouts',
- description: 'Timeouts and optimization',
-}
-
-By default, Supabase limits the maximum statement execution time to _8 seconds_ for users accessing the API. Additionally, all users are subject to a global limit of _2 minutes_. This serves as a backstop against resource exhaustion due to either poorly written queries, or abusive usage.
-
-### Changing the default timeout
-
-The timeout values were picked as a reasonable default for the majority of use-cases, but can be modified using the [`alter role`](https://www.postgresql.org/docs/current/sql-alterrole.html) statement:
-
-```sql
--- For API users
-alter role authenticator set statement_timeout = '15s';
-```
-
-You can also update the statement timeout for a session:
-
-```sql
-set statement_timeout to 60000; -- 1 minute in milliseconds
-```
-
-### Statement Optimization
-
-All Supabase projects come with the [`pg_stat_statements`](https://www.postgresql.org/docs/current/pgstatstatements.html) extension installed, which tracks planning and execution statistics for all statements executed against it. These statistics can be used in order to diagnose the performance of your project.
-
-This data can further be used in conjunction with the [`explain`](https://www.postgresql.org/docs/current/using-explain.html) functionality of Postgres to optimize your usage.
-
-export const Page = ({ children }) =>
-
-export default Page
diff --git a/apps/docs/pages/guides/database/webhooks.mdx b/apps/docs/pages/guides/database/webhooks.mdx
index 3849f0aa30e90..3c821f8a77bac 100644
--- a/apps/docs/pages/guides/database/webhooks.mdx
+++ b/apps/docs/pages/guides/database/webhooks.mdx
@@ -4,24 +4,21 @@ export const meta = {
id: 'webhooks',
title: 'Database Webhooks',
description: 'Trigger external payloads on database events.',
- video: 'https://www.youtube.com/v/codAs9-NeHM',
+ subtitle: 'Trigger external payloads on database events.',
+ tocVideo: 'codAs9-NeHM',
}
Database Webhooks allow you to send real-time data from your database to another system whenever a table event occurs.
You can hook into three table events: `INSERT`, `UPDATE`, and `DELETE`. All events are fired _after_ a database row is changed.
+## 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.
This video demonstrates how you can create a new customer in Stripe each time a row is inserted into a `profiles` table:
-
-
-Database Webhooks were previously known as Function Hooks.
-
-
-