From ca63127db194b3533487a092c2d7620933d0113c Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Wed, 26 Oct 2022 14:39:53 +0200 Subject: [PATCH] Docs: Adds pgnet and database webhooks (#9803) * Adds pgnet and database webhooks * fixes name of pg_net * Adds the cron extension * Update apps/reference/docs/guides/database/database-webhooks.mdx Co-authored-by: Steve Chavez * Update apps/reference/docs/guides/database/extensions/pgnet.mdx Co-authored-by: Steve Chavez * Update apps/reference/docs/guides/database/database-webhooks.mdx Co-authored-by: dng * Update apps/reference/docs/guides/database/database-webhooks.mdx Co-authored-by: dng * Update apps/reference/docs/guides/database/database-webhooks.mdx Co-authored-by: dng * Update apps/reference/nav/_referenceSidebars.js Co-authored-by: dng * Update apps/reference/nav/_referenceSidebars.js Co-authored-by: dng * Update apps/reference/docs/guides/database/extensions/pgnet.mdx * Update apps/reference/docs/guides/database/extensions/pgnet.mdx * Update apps/reference/docs/guides/database/database-webhooks.mdx * Update apps/reference/docs/guides/database/database-webhooks.mdx * Update apps/reference/docs/guides/database/extensions/pgnet.mdx Co-authored-by: Steve Chavez Co-authored-by: dng --- .../guides/database/database-webhooks.mdx | 74 +++++ .../guides/database/extensions/pgcron.mdx | 99 +++++++ .../docs/guides/database/extensions/pgnet.mdx | 254 ++++++++++++++++++ apps/reference/nav/_referenceSidebars.js | 5 +- 4 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 apps/reference/docs/guides/database/database-webhooks.mdx create mode 100644 apps/reference/docs/guides/database/extensions/pgcron.mdx create mode 100644 apps/reference/docs/guides/database/extensions/pgnet.mdx diff --git a/apps/reference/docs/guides/database/database-webhooks.mdx b/apps/reference/docs/guides/database/database-webhooks.mdx new file mode 100644 index 00000000000..6828a0bbc7b --- /dev/null +++ b/apps/reference/docs/guides/database/database-webhooks.mdx @@ -0,0 +1,74 @@ +--- +id: webhooks +title: 'Database Webhooks' +description: Trigger external payloads on database events. +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +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. + +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: + +:::note + +Database Webhooks were previously known as Function Hooks. + +::: + +
+ +
+ +## Creating a webhook + +1. Create a new [Database Webhook](https://app.supabase.com/project/_/database/hooks) in the Dashboard. +1. Give your Webhook a name. +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. + +We currently support HTTP webhooks. These are sent as a `POST` request with a JSON payload. + +## Payload + +The payload is automatically generated from the underlying table record: + +```typescript +type InsertPayload = { + type: 'INSERT' + table: string + schema: string + record: TableRecord + old_record: null +} +type UpdatePayload = { + type: 'UPDATE' + table: string + schema: string + record: TableRecord + old_record: TableRecord +} +type DeletePayload = { + type: 'DELETE' + table: string + schema: string + record: null + old_record: TableRecord +} +``` + +## Resources + +- [pg_net](/docs/guides/database/extensions/pgnet): an async networking extension for PostgreSQL diff --git a/apps/reference/docs/guides/database/extensions/pgcron.mdx b/apps/reference/docs/guides/database/extensions/pgcron.mdx new file mode 100644 index 00000000000..e7032bf9bd9 --- /dev/null +++ b/apps/reference/docs/guides/database/extensions/pgcron.mdx @@ -0,0 +1,99 @@ +--- +id: pgcron +title: 'pg_cron: Job Scheduling' +description: 'pgnet: a simple cron-based job scheduler for PostgreSQL that runs inside the database.' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +The `pg_cron` extension is a simple cron-based job scheduler for PostgreSQL that runs inside the database. + +## Usage + +### Enable the extension + + + + + +1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +2. Click on **Extensions** in the sidebar. +3. Search for "pg_cron" and enable the extension. + + + + +```sql +-- Example: enable the "pg_cron" extension +create extension pg_cron with schema extensions; + +-- If you're planning to use a non-superuser role to schedule jobs, +-- ensure that they are granted access to the cron schema and its underlying objects beforehand. +-- Failure to do so would result in jobs by these roles to not run at all. + +grant usage on schema cron to {{DB user}}; +grant all privileges on all tables in schema cron to {{DB user}}; + + +-- Example: disable the "pg_cron" extension +drop extension if exists pg_cron; +``` + + + + +### Syntax + +The schedule uses the standard cron syntax, in which \* means "run every time period", and a specific number means "but only at this time": + +```bash + ┌───────────── min (0 - 59) + │ ┌────────────── hour (0 - 23) + │ │ ┌─────────────── day of month (1 - 31) + │ │ │ ┌──────────────── month (1 - 12) + │ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to + │ │ │ │ │ Saturday, or use names; 7 is also Sunday) + │ │ │ │ │ + │ │ │ │ │ + * * * * * +``` + +## Examples + +### Delete data every week + +Delete old data on Saturday at 3:30am (GMT): + +```sql +select cron.schedule ( + 'webhook-every-minute', -- name of the cron job + '* * * * *', -- every minute + $$ delete from events where event_time < now() - interval '1 week' $$ +); +``` + +### Run a vacuum every day + +Vacuum every day at 3:00am (GMT) + +```sql +SELECT cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM'); +``` + +### Unschedule a job + +Unschedules a job called `'nightly-vacuum'` + +```sql +SELECT cron.unschedule('nightly-vacuum'); +``` + +## Resources + +- [pg_cron GitHub Repository](https://github.com/citusdata/pg_cron) diff --git a/apps/reference/docs/guides/database/extensions/pgnet.mdx b/apps/reference/docs/guides/database/extensions/pgnet.mdx new file mode 100644 index 00000000000..341af926e44 --- /dev/null +++ b/apps/reference/docs/guides/database/extensions/pgnet.mdx @@ -0,0 +1,254 @@ +--- +id: pgnet +title: 'pg_net: Async Networking' +description: 'pg_net: an async networking extension for PostgreSQL.' +--- + +import Tabs from '@theme/Tabs' +import TabItem from '@theme/TabItem' + +:::caution + +The pg_net API is in beta. Functions signatures may change. + +::: + +[pg_net](https://github.com/supabase/pg_net/) is a PostgreSQL extension exposing a SQL interface for async networking with a focus on scalability and UX. + +It differs from the `http` extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers). + +## Usage + +### Enable the extension + + + + + +1. Go to the [Database](https://app.supabase.com/project/_/database/tables) page in the Dashboard. +2. Click on **Extensions** in the sidebar. +3. Search for "pg_net" and enable the extension. + + + + +```sql +-- Example: enable the "pg_net" extension +create schema if not exists net; +create extension pg_net with schema net; + +-- Example: disable the "plv8" extension +drop extension if exists pg_net; +drop schema net; +``` + +Even though the SQL code is `create extension`, this is the equivalent of "enabling the extension". +To disable an extension, call `drop extension`. + +Procedural languages are automatically installed within `pg_catalog`, so you don't need to specify a schema. + + + + +## `http_get` {#http_get} + +Creates an HTTP GET request returning the request's ID. HTTP requests are not started until the transaction is committed. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function. + +::: + +```sql +net.http_get( + -- url for the request + url text, + -- key/value pairs to be url encoded and appended to the `url` + params jsonb default '{}'::jsonb, + -- key/values to be included in request headers + headers jsonb default '{}'::jsonb, + -- WARNING: this is currently ignored, so there is no timeout + -- the maximum number of milliseconds the request may take before being cancelled + timeout_milliseconds int default 1000 +) + -- request_id reference + returns bigint + + strict + volatile + parallel safe + language plpgsql +``` + +### Usage + +```sql +select net.http_get('https://news.ycombinator.com') as request_id; +request_id +---------- + 1 +(1 row) +``` + +After triggering `http_get`, use [`http_get_result`](#http_get_result) to get the result of the request. + +## `http_post` {#http_post} + +Creates an HTTP POST request with a JSON body, returning the request's ID. HTTP requests are not started until the transaction is committed. + +The body's character set encoding matches the database's `server_encoding` setting. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function + +::: + +```sql +net.http_post( + -- url for the request + url text, + -- body of the POST request + body jsonb default '{}'::jsonb, + -- key/value pairs to be url encoded and appended to the `url` + params jsonb default '{}'::jsonb, + -- key/values to be included in request headers + headers jsonb default '{"Content-Type": "application/json"}'::jsonb, + -- WARNING: this is currently ignored, so there is no timeout + -- the maximum number of milliseconds the request may take before being cancelled + timeout_milliseconds int default 1000 +) + -- request_id reference + returns bigint + + volatile + parallel safe + language plpgsql +``` + +### Usage + +```sql +select + net.http_post( + url:='https://httpbin.org/post', + body:='{"hello": "world"}'::jsonb + ) as request_id; +request_id +---------- + 1 +(1 row) +``` + +After triggering `http_post`, use [`http_get_result`](#http_get_result) to get the result of the request. + +## `http_collect_response` {#http_collect_response} + +Given a `request_id` reference, retrieves the response. + +When `async:=false` is set it is recommended that [statement_timeout](https://www.postgresql.org/docs/13/runtime-config-client.html) is set for the maximum amount of time the caller is willing to wait in case the response is slow to populate. + +### Signature + +:::caution + +This is a Postgres SECURITY DEFINER function + +::: + +```sql +net.http_collect_response( + -- request_id reference + request_id bigint, + -- when `true`, return immediately. when `false` wait for the request to complete before returning + async bool default true +) + -- http response composite wrapped in a result type + returns net.http_response_result + + strict + volatile + parallel safe +``` + +### Usage + +:::caution + +`net.http_collect_response` must be in a separate transaction from the calls to `net.http_` + +::: + +```sql +select + net.http_post( + url:='https://httpbin.org/post', + body:='{"hello": "world"}'::jsonb + ) as request_id; +request_id +---------- + 1 +(1 row) + +select * from net.http_collect_response(1, async:=false); +status | message | response +--------+---------+---------- +SUCCESS ok ( + status_code := 200, + headers := '{"date": ...}', + body := '{"args": ...}' + )::net.http_response_result + + +select + (response).body::json +from + net.http_collect_response(request_id:=1); + body +------------------------------------------------------------------- + { + "args": {}, + "data": "{\"hello\": \"world\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Content-Length": "18", + "Content-Type": "application/json", + "Host": "httpbin.org", + "User-Agent": "pg_net/0.2", + "X-Amzn-Trace-Id": "Root=1-61031a5c-7e1afeae69bffa8614d8e48e" + }, + "json": { + "hello": "world" + }, + "origin": "135.63.38.488", + "url": "https://httpbin.org/post" + } +(1 row) +``` + +Where `response` is a composite: + +```sql +status_code integer +headers jsonb +body text +``` + +Possible values for `net.http_response_result.status` are `('PENDING', 'SUCCESS', 'ERROR')` + +## Resources + +- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/) +- Official Docs: [supabase.github.io/pg_net](https://supabase.github.io/pg_net/) diff --git a/apps/reference/nav/_referenceSidebars.js b/apps/reference/nav/_referenceSidebars.js index f88427304a1..304f9347d96 100644 --- a/apps/reference/nav/_referenceSidebars.js +++ b/apps/reference/nav/_referenceSidebars.js @@ -124,6 +124,7 @@ const sidebars = { 'guides/database/connecting-to-postgres', 'guides/database/tables', 'guides/database/functions', + 'guides/database/webhooks', 'guides/database/full-text-search', 'guides/database/migrating-between-projects', // 'guides/database/json', @@ -135,9 +136,11 @@ const sidebars = { collapsed: true, items: [ 'guides/database/extensions', - 'guides/database/extensions/plv8', 'guides/database/extensions/http', + 'guides/database/extensions/pgcron', + 'guides/database/extensions/pgnet', 'guides/database/extensions/pgtap', + 'guides/database/extensions/plv8', 'guides/database/extensions/uuid-ossp', ], },