mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 03:45:06 +03:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## Problem The Docs E2E link checker found broken links throughout docs, starting with `phone-login.mdx` pointing to `/docs/guides/cli/config` (404). Old links like `/docs/guides/cli/config` still work on the live site because `supabase.com` has redirects set up for them, but these links break on the docs preview site, which is what the E2E check tests against. These issues look clean on the live site, and I didn't catch them in my first pass because I was testing production instead of the preview. The E2E check only tests the ~20 pages a given PR happens to touch, so fixing the pages it flagged kept exposing more of the same problem one page at a time as each fix pulled in a new file. To stop chasing this incrementally, I cross-referenced every `/docs/guides/*` and `/docs/reference/*` redirect source in `apps/www/lib/redirects.js` against actual usage across all of `apps/docs`, and verified each candidate against the live preview. ## Solution Rather than updating the Docs E2E link checker, this PR resolves the links. **Why:** we own these docs, so keeping the links clean without redirects is keeping the house maintained. See [Broken Window Theory](https://blog.codinghorror.com/the-broken-window-theory/). Updated every link still using an old path to point straight at the current page instead of relying on a redirect. This covers old links like: - `/docs/guides/cli/config` → `/docs/guides/local-development/cli/config` - `/docs/guides/cli/getting-started` → `/docs/guides/local-development/cli/getting-started` - `/docs/guides/cli/local-development` → `/docs/guides/local-development/database-migrations` - `/docs/guides/cli/managing-environments` → `/docs/guides/deployment/managing-environments` - `/docs/guides/cli/seeding-your-database` → `/docs/guides/local-development/seeding-your-database` - bare `/docs/guides/cli` → `/docs/guides/local-development` - `/docs/guides/platform/compute-add-ons` → `/docs/guides/platform/compute-and-disk` - `/docs/guides/platform/shared-responsibility-model` → `/docs/guides/deployment/shared-responsibility-model` - `/docs/guides/database` → `/docs/guides/database/overview` - `/docs/reference/javascript`, `/docs/reference/dart`, `/docs/reference/kotlin`, `/docs/reference/python`, `/docs/reference/csharp` → their `/introduction` pages (the redirect's own destination, `/start`, turned out to be dead even on production — a separate bug in `redirects.js` I didn't touch here) - and about 35 more of the same pattern, listed in the commit messages Also fixed a handful of dead heading anchors found along the way (links that resolve to the right page but point at a `#section` that got renamed or moved), including the original `#bigquery` anchor and a few in `connecting-to-postgres.mdx` where content moved to its own dedicated page. Left alone on purpose: - `content/guides/cli.mdx` — this page has no route in the docs app at all (no `app/guides/cli/` directory), so it 404s even in production before the `www` redirect ever fires. Fixing its internal link wouldn't change that; it needs an actual routing/content decision, not a link fix. - A few candidates that already resolve fine as-is (`pg_partman`, bare `/docs/reference/api`, bare `/docs/reference/cli`) — confirmed via curl, left untouched. ## Manual testing 1. Confirmed every new link target actually exists by checking the destination file/page and matching heading anchors. 2. Cross-referenced every `/docs/guides/*` and `/docs/reference/*` redirect source in `apps/www/lib/redirects.js` against real usage in `apps/docs`, and curl-verified each old path (404) and new path (200) against the live PR preview before fixing it. 3. Ran the Docs E2E link checker locally against changed pages. 4. Spot-checked the original broken link from CI (`/docs/guides/cli/config`) to confirm it now points to a working page.
450 lines
12 KiB
Plaintext
450 lines
12 KiB
Plaintext
---
|
|
id: 'pg_net'
|
|
title: 'pg_net: Async Networking'
|
|
description: 'pg_net: an async networking extension for Postgres.'
|
|
---
|
|
|
|
<Admonition type="caution">
|
|
|
|
The pg_net API is in beta. Functions signatures may change.
|
|
|
|
</Admonition>
|
|
|
|
[pg_net](https://github.com/supabase/pg_net/) enables Postgres to make asynchronous HTTP/HTTPS requests in SQL. It differs from the [`http`](/docs/guides/database/extensions/http) extension in that it is asynchronous by default. This makes it useful in blocking functions (like triggers).
|
|
|
|
It eliminates the need for servers to continuously poll for database changes and instead allows the database to proactively notify external resources about significant events.
|
|
|
|
## Enable the extension
|
|
|
|
<Tabs
|
|
scrollable
|
|
size="small"
|
|
type="underlined"
|
|
defaultActiveId="dashboard"
|
|
queryGroup="database-method"
|
|
>
|
|
<TabPanel id="dashboard" label="Dashboard">
|
|
|
|
1. Go to the [Database](/dashboard/project/_/database/tables) page in the Dashboard.
|
|
2. Click on **Extensions** in the sidebar.
|
|
3. Search for "pg_net" and enable the extension.
|
|
|
|
</TabPanel>
|
|
<TabPanel id="sql" label="SQL">
|
|
|
|
```sql
|
|
-- Example: enable the "pg_net" extension.
|
|
create extension pg_net;
|
|
-- Note: The extension creates its own schema/namespace named "net" to avoid naming conflicts.
|
|
|
|
-- Example: disable the "pg_net" 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.
|
|
|
|
</TabPanel>
|
|
</Tabs>
|
|
|
|
## `http_get`
|
|
|
|
Creates an HTTP GET request returning the request's ID. HTTP requests are not started until the transaction is committed.
|
|
|
|
### Signature [#get-signature]
|
|
|
|
<Admonition type="caution">
|
|
|
|
This is a Postgres [SECURITY DEFINER](/docs/guides/database/postgres/row-level-security#use-security-definer-functions) function.
|
|
|
|
</Admonition>
|
|
|
|
```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,
|
|
-- the maximum number of milliseconds the request may take before being canceled
|
|
timeout_milliseconds int default 2000
|
|
)
|
|
-- request_id reference
|
|
returns bigint
|
|
|
|
strict
|
|
volatile
|
|
parallel safe
|
|
language plpgsql
|
|
```
|
|
|
|
### Usage [#get-usage]
|
|
|
|
```sql
|
|
select
|
|
net.http_get('https://news.ycombinator.com')
|
|
as request_id;
|
|
request_id
|
|
----------
|
|
1
|
|
(1 row)
|
|
```
|
|
|
|
## `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 [#post-signature]
|
|
|
|
<Admonition type="caution">
|
|
|
|
This is a Postgres [SECURITY DEFINER](/docs/guides/database/postgres/row-level-security#use-security-definer-functions) function
|
|
|
|
</Admonition>
|
|
|
|
```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,
|
|
-- the maximum number of milliseconds the request may take before being canceled
|
|
timeout_milliseconds int default 2000
|
|
)
|
|
-- request_id reference
|
|
returns bigint
|
|
|
|
volatile
|
|
parallel safe
|
|
language plpgsql
|
|
```
|
|
|
|
### Usage [#post-usage]
|
|
|
|
```sql
|
|
select
|
|
net.http_post(
|
|
url:='https://httpbin.org/post',
|
|
body:='{"hello": "world"}'::jsonb
|
|
) as request_id;
|
|
request_id
|
|
----------
|
|
1
|
|
(1 row)
|
|
```
|
|
|
|
## `http_delete`
|
|
|
|
Creates an HTTP DELETE request, returning the request's ID. HTTP requests are not started until the transaction is committed.
|
|
|
|
### Signature [#post-signature]
|
|
|
|
<Admonition type="caution">
|
|
|
|
This is a Postgres [SECURITY DEFINER](/docs/guides/database/postgres/row-level-security#use-security-definer-functions) function
|
|
|
|
</Admonition>
|
|
|
|
```sql
|
|
net.http_delete(
|
|
-- 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,
|
|
-- the maximum number of milliseconds the request may take before being canceled
|
|
timeout_milliseconds int default 2000
|
|
)
|
|
-- request_id reference
|
|
returns bigint
|
|
|
|
strict
|
|
volatile
|
|
parallel safe
|
|
language plpgsql
|
|
security definer
|
|
```
|
|
|
|
### Usage [#delete-usage]
|
|
|
|
```sql
|
|
select
|
|
net.http_delete(
|
|
'https://dummy.restapiexample.com/api/v1/delete/2'
|
|
) as request_id;
|
|
----------
|
|
1
|
|
(1 row)
|
|
```
|
|
|
|
## Analyzing responses
|
|
|
|
Waiting requests are stored in the `net.http_request_queue` table. Upon execution, they are deleted.
|
|
|
|
```sql
|
|
CREATE UNLOGGED TABLE
|
|
net.http_request_queue (
|
|
id bigint NOT NULL DEFAULT nextval('net.http_request_queue_id_seq'::regclass),
|
|
method text NOT NULL,
|
|
url text NOT NULL,
|
|
headers jsonb NOT NULL,
|
|
body bytea NULL,
|
|
timeout_milliseconds integer NOT NULL
|
|
)
|
|
```
|
|
|
|
Once a response is returned, by default, it is stored for 6 hours in the `net._http_response` table.
|
|
|
|
```sql
|
|
CREATE UNLOGGED TABLE
|
|
net._http_response (
|
|
id bigint NULL,
|
|
status_code integer NULL,
|
|
content_type text NULL,
|
|
headers jsonb NULL,
|
|
content text NULL,
|
|
timed_out boolean NULL,
|
|
error_msg text NULL,
|
|
created timestamp with time zone NOT NULL DEFAULT now()
|
|
)
|
|
```
|
|
|
|
The responses can be observed with the following query:
|
|
|
|
```sql
|
|
select * from net._http_response;
|
|
```
|
|
|
|
The data can also be observed in the `net` schema with the [Supabase Dashboard's SQL Editor](/dashboard/project/_/editor)
|
|
|
|
## Debugging requests
|
|
|
|
### Inspecting request data
|
|
|
|
The [Postman Echo API](https://documenter.getpostman.com/view/5025623/SWTG5aqV) returns a response with the same body and content
|
|
as the request. It can be used to inspect the data being sent.
|
|
|
|
Sending a post request to the echo API
|
|
|
|
```sql
|
|
select
|
|
net.http_post(
|
|
url := 'https://postman-echo.com/post',
|
|
body := '{"key1": "value", "key2": 5}'::jsonb
|
|
) as request_id;
|
|
```
|
|
|
|
Inspecting the echo API response content to ensure it contains the right body
|
|
|
|
```sql
|
|
select
|
|
"content"
|
|
from net._http_response
|
|
where id = <request_id>
|
|
-- returns information about the request
|
|
-- including the body sent: {"key": "value", "key": 5}
|
|
```
|
|
|
|
Alternatively, by wrapping a request in a [database function](/docs/guides/database/functions), sent row data can be logged or returned for inspection and debugging.
|
|
|
|
```sql
|
|
create or replace function debugging_example (row_id int)
|
|
returns jsonb as $$
|
|
declare
|
|
-- Store payload data
|
|
row_data_var jsonb;
|
|
begin
|
|
-- Retrieve row data and convert to JSON
|
|
select to_jsonb("<example_table>".*) into row_data_var
|
|
from "<example_table>"
|
|
where "<example_table>".id = row_id;
|
|
|
|
-- Initiate HTTP POST request to URL
|
|
perform
|
|
net.http_post(
|
|
url := 'https://postman-echo.com/post',
|
|
-- Use row data as payload
|
|
body := row_data_var
|
|
) as request_id;
|
|
|
|
-- Optionally Log row data or other data for inspection in Supabase Dashboard's Postgres Logs
|
|
raise log 'Logging an entire row as JSON (%)', row_data_var;
|
|
|
|
-- return row data to inspect
|
|
return row_data_var;
|
|
|
|
-- Handle exceptions here if needed
|
|
exception
|
|
when others then
|
|
raise exception 'An error occurred: %', SQLERRM;
|
|
end;
|
|
$$ language plpgsql;
|
|
|
|
-- calling function
|
|
select debugging_example(<row_id>);
|
|
```
|
|
|
|
### Inspecting failed requests
|
|
|
|
Finds all failed requests
|
|
|
|
```sql
|
|
select
|
|
*
|
|
from net._http_response
|
|
where "status_code" >= 400 or "error_msg" is not null
|
|
order by "created" desc;
|
|
```
|
|
|
|
## Configuration
|
|
|
|
<Admonition type="note" title="Must be on pg_net v0.12.0 or above to reconfigure ">
|
|
|
|
Supabase supports reconfiguring pg*net starting from v0.12.0+. For the latest release, initiate a Postgres upgrade in the [Infrastructure Settings](/dashboard/project/*/settings/infrastructure).
|
|
|
|
</Admonition>
|
|
|
|
The extension is configured to reliably execute up to 200 requests per second. The response messages are stored for only 6 hours to prevent needless buildup. The default behavior can be modified by rewriting config variables.
|
|
|
|
### Get current settings
|
|
|
|
```sql
|
|
select
|
|
"name",
|
|
"setting"
|
|
from pg_settings
|
|
where "name" like 'pg_net%';
|
|
```
|
|
|
|
### Alter settings
|
|
|
|
You must change the `pg_net` settings at the system level.
|
|
|
|
<Admonition type="note">
|
|
|
|
Changing these settings requires superuser privileges. Contact Support to have the required permission granted for the parameter you want to change, e.g.:
|
|
|
|
```sql
|
|
grant alter system on parameter pg_net.ttl to postgres;
|
|
```
|
|
|
|
</Admonition>
|
|
|
|
Once the privilege is assigned, apply the setting at the system level and restart the background worker:
|
|
|
|
```sql
|
|
alter system set pg_net.ttl to '24 hours';
|
|
select net.worker_restart();
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Invoke a Supabase Edge Function
|
|
|
|
Make a POST request to a Supabase Edge Function with auth header and JSON body payload:
|
|
|
|
```sql
|
|
select
|
|
net.http_post(
|
|
url:='https://project-ref.supabase.co/functions/v1/function-name',
|
|
headers:='{"Content-Type": "application/json", "apikey": "<SUPABASE_PUBLISHABLE_KEY>"}'::jsonb,
|
|
body:='{"name": "pg_net"}'::jsonb
|
|
) as request_id;
|
|
```
|
|
|
|
### Call an endpoint every minute with [pg_cron](/docs/guides/database/extensions/pg_cron)
|
|
|
|
The pg_cron extension enables Postgres to become its own cron server. With it you can schedule regular calls with up to a minute precision to endpoints.
|
|
|
|
```sql
|
|
select cron.schedule(
|
|
'cron-job-name',
|
|
'* * * * *', -- Executes every minute (cron syntax)
|
|
$$
|
|
-- SQL query
|
|
select "net"."http_post"(
|
|
-- URL of Edge function
|
|
url:='https://project-ref.supabase.co/functions/v1/function-name',
|
|
headers:='{"apikey": "<SUPABASE_PUBLISHABLE_KEY>"}'::jsonb,
|
|
body:='{"name": "pg_net"}'::jsonb
|
|
) as "request_id";
|
|
$$
|
|
);
|
|
```
|
|
|
|
### Execute pg_net in a trigger
|
|
|
|
Make a call to an external endpoint when a trigger event occurs.
|
|
|
|
```sql
|
|
-- function called by trigger
|
|
create or replace function <function_name>()
|
|
returns trigger
|
|
language plpgSQL
|
|
as $$
|
|
begin
|
|
-- calls pg_net function net.http_post
|
|
-- sends request to postman API
|
|
perform "net"."http_post"(
|
|
'https://postman-echo.com/post'::text,
|
|
jsonb_build_object(
|
|
'old_row', to_jsonb(old.*),
|
|
'new_row', to_jsonb(new.*)
|
|
),
|
|
headers:='{"Content-Type": "application/json"}'::jsonb
|
|
) as request_id;
|
|
return new;
|
|
END $$;
|
|
|
|
-- trigger for table update
|
|
create trigger <trigger_name>
|
|
after update on <table_name>
|
|
for each row
|
|
execute function <function_name>();
|
|
```
|
|
|
|
### Send multiple table rows in one request
|
|
|
|
```sql
|
|
with "selected_table_rows" as (
|
|
select
|
|
-- Converts all the rows into a JSONB array
|
|
jsonb_agg(to_jsonb(<table_name>.*)) as JSON_payload
|
|
from <table_name>
|
|
-- good practice to LIMIT the max amount of rows
|
|
)
|
|
select
|
|
net.http_post(
|
|
url := 'https://postman-echo.com/post'::text,
|
|
body := JSON_payload
|
|
) AS request_id
|
|
FROM "selected_table_rows";
|
|
```
|
|
|
|
More examples can be seen on the [Extension's GitHub page](https://github.com/supabase/pg_net/)
|
|
|
|
## Limitations
|
|
|
|
- To improve speed and performance, the requests and responses are stored in [unlogged tables](https://pgpedia.info/u/unlogged-table.html), which are not preserved during a crash or unclean shutdown.
|
|
- By default, response data is saved for only 6 hours
|
|
- Can only make POST requests with JSON data. No other data formats are supported
|
|
- Intended to handle at most 200 requests per second. Increasing the rate can introduce instability
|
|
- Does not have support for PATCH/PUT requests
|
|
- Can only work with one database at a time. It defaults to the `postgres` database.
|
|
|
|
## Resources
|
|
|
|
- Source code: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/)
|
|
- Official Docs: [github.com/supabase/pg_net](https://github.com/supabase/pg_net/)
|