OpenAPI
OpenAPI is a specification for describing HTTP APIs. The OpenAPI Wrapper is a generic WebAssembly (Wasm) foreign data wrapper that can connect to any REST API with an OpenAPI 3.0+ specification.
This wrapper allows you to query any REST API endpoint as a PostgreSQL foreign table, with support for path parameters, pagination, POST-for-read endpoints, and automatic schema import.
Available Versions
| Version | Wasm Package URL | Checksum | Required Wrappers Version |
|---|---|---|---|
| 0.2.0 | https://github.com/supabase/wrappers/releases/download/wasm_openapi_fdw_v0.2.0/openapi_fdw.wasm |
f0d4d6e50f7c519a66363bd8bdbe1ea8086ca810ca14b43fb0ed18b64acdf6aa |
>=0.5.0 |
| 0.1.4 | https://github.com/supabase/wrappers/releases/download/wasm_openapi_fdw_v0.1.4/openapi_fdw.wasm |
dd434f8565b060b181d1e69e1e4d5c8b9c3ac5ca444056d3c2fb939038d308fe |
>=0.5.0 |
Preparation
Before you can query an API, you need to enable the Wrappers extension and store your credentials in Postgres.
Enable Wrappers
Make sure the wrappers extension is installed on your database:
1 | |
Enable the OpenAPI Wrapper
Enable the Wasm foreign data wrapper:
1 2 3 | |
Store credentials (optional)
By default, Postgres stores FDW credentials inside pg_catalog.pg_foreign_server in plain text. Anyone with access to this table will be able to view these credentials. Wrappers is designed to work with Vault, which provides an additional level of security for storing credentials. We recommend using Vault to store your credentials.
1 2 3 4 5 6 | |
Connecting to an API
We need to provide Postgres with the credentials to access the API and any additional options. We can do this using the create server command:
1 2 3 4 5 6 7 8 9 10 | |
1 2 3 4 5 6 7 8 9 10 | |
Server Options
| Option | Required | Description |
|---|---|---|
fdw_package_* |
Yes | Standard Wasm FDW package metadata. See Available Versions. |
base_url |
Yes* | Base URL for the API (e.g., https://api.example.com/v1). *Optional if spec_url or spec_json provides servers. |
spec_url |
No | URL to the OpenAPI specification (JSON or YAML). Required for IMPORT FOREIGN SCHEMA. Mutually exclusive with spec_json. |
spec_json |
No | Inline OpenAPI 3.0+ JSON spec for IMPORT FOREIGN SCHEMA. Mutually exclusive with spec_url. Useful when the API doesn't publish a spec URL. |
api_key |
No | API key for authentication. |
api_key_id |
No | Vault secret key ID storing the API key. Use instead of api_key. |
api_key_header |
No | Header name for API key (default: Authorization). |
api_key_prefix |
No | Prefix for API key value (default: Bearer for Authorization header). |
api_key_location |
No | Where to send the API key: header (default), query, or cookie. |
bearer_token |
No | Bearer token for authentication (alternative to api_key). |
bearer_token_id |
No | Vault secret key ID storing the bearer token. |
user_agent |
No | Custom User-Agent header value. |
accept |
No | Custom Accept header for content negotiation (e.g., application/geo+json). |
headers |
No | Custom headers as JSON object (e.g., '{"X-Custom": "value"}'). |
include_attrs |
No | Include attrs jsonb column in IMPORT FOREIGN SCHEMA output (default: 'true'). Set to 'false' to omit. |
page_size |
No | Default page size for pagination (0 = no automatic limit). |
page_size_param |
No | Query parameter name for page size (default: limit). |
cursor_param |
No | Query parameter name for pagination cursor (default: after). |
max_pages |
No | Maximum pages per scan to prevent infinite pagination loops (default: 1000). |
max_response_bytes |
No | Maximum response body size in bytes (default: 52428800 / 50 MiB). |
debug |
No | Emit HTTP request details and scan stats via PostgreSQL INFO messages when set to 'true' or '1'. |
Create a schema
We recommend creating a schema to hold all the foreign tables:
1 | |
Creating Foreign Tables
Manual Table Creation
Create foreign tables manually by specifying the endpoint and columns:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Table Options
| Option | Required | Description |
|---|---|---|
endpoint |
Yes | API endpoint path (e.g., /users, /users/{user_id}/posts). |
rowid_column |
No | Column used as row identifier for single-resource access and modifications (default: id). |
response_path |
No | JSON pointer to extract data array from response (e.g., /data, /results). |
object_path |
No | JSON pointer to extract nested object from each row (e.g., /properties for GeoJSON). |
cursor_path |
No | JSON pointer to pagination cursor in response. |
cursor_param |
No | Override server-level cursor parameter name. |
page_size_param |
No | Override server-level page size parameter name. |
page_size |
No | Override server-level page size. |
method |
No | HTTP method for this endpoint. Use POST for read-via-POST endpoints (default: GET). |
request_body |
No | Request body string for POST endpoints. |
Automatic Schema Import
If you provide a spec_url or spec_json in the server options, you can automatically import table definitions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Note
IMPORT FOREIGN SCHEMA only generates tables for non-parameterized GET endpoints (e.g., /users, /orders). Endpoints with path parameters like /users/{user_id}/posts are skipped because they require WHERE clause values at query time. Create these tables manually using the endpoint option with {param} placeholders — see Path Parameters for examples.
Path Parameters
The OpenAPI FDW supports path parameter substitution. Define parameters in the endpoint template using {param_name} syntax, and provide values via WHERE clauses:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Multiple Path Parameters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Query Pushdown
Single Resource Access
When filtering by the rowid_column, the FDW automatically requests a single resource:
1 2 | |
Query Parameters
Other WHERE clause filters are passed as query parameters:
1 2 | |
Columns used as query or path parameters always return the value from the WHERE clause, even if the API response contains the same field with different casing. This ensures PostgreSQL's post-filter always passes.
LIMIT Pushdown
When your query includes a LIMIT, the FDW uses it as the page_size for the first API request, reducing unnecessary data transfer:
1 2 | |
POST-for-Read Endpoints
Some APIs use POST requests for read operations (e.g., search or query endpoints). Use the method and request_body table options:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Debug Mode
Enable debug mode to see HTTP request details and scan statistics in PostgreSQL INFO messages:
1 2 3 4 5 6 7 8 9 10 | |
Debug output includes:
- HTTP method and URL for each request
- Response status code and body size
- Total rows fetched and pages retrieved
- Pagination details
Pagination
The FDW automatically handles pagination. It supports:
- Cursor-based pagination - Uses
cursor_paramandcursor_path - URL-based pagination - Follows
nextlinks in response body (e.g.,/links/next,/meta/pagination/next) Linkheader pagination - Follows RFC 8288Link: <...>; rel="next"response headers (GitHub, GitLab, and most REST APIs)- Offset-based pagination - Auto-detected from common patterns
Configuring Pagination
1 2 3 4 5 6 7 8 9 10 11 12 | |
1 2 3 4 5 6 7 8 9 10 | |
GeoJSON Support
For APIs that return GeoJSON, use object_path to extract properties:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Supported Data Types
| Postgres Type | JSON Type |
|---|---|
| text | string |
| boolean | boolean |
| smallint* | number |
| integer | number |
| bigint | number |
| real | number |
| double precision | number |
| numeric* | number |
| date | string (ISO 8601) |
| timestamp* | string (ISO 8601) |
| timestamptz | string (ISO 8601) |
| jsonb | object/array |
| uuid | string |
* Types marked with an asterisk work when you define tables manually, but IMPORT FOREIGN SCHEMA won't generate columns with these types automatically.
The attrs Column
Any foreign table can include an attrs column of type jsonb to capture the entire raw JSON response for each row:
1 2 3 4 5 6 7 | |
Limitations
- Read-only: This FDW only supports SELECT operations. INSERT, UPDATE, and DELETE are not supported at this time.
- No transactions: Each SQL statement results in immediate HTTP requests; there is no transactional grouping.
- Authentication: Currently supports API Key and Bearer Token authentication. OAuth flows are not supported.
- OpenAPI version: Only OpenAPI 3.0+ specifications are supported (not Swagger 2.0).
Automatic Retries
The FDW automatically retries transient HTTP errors up to 3 times:
- HTTP 429 (Rate Limit), 502 (Bad Gateway), 503 (Service Unavailable)
- Retry-After header: Respects server-specified delay when provided
- Exponential backoff: Falls back to 1s, 2s, 4s delays when no Retry-After header is present
For APIs with very strict rate limits, consider using materialized views to cache results.
Examples
For additional real-world examples with multiple tables, pagination, and advanced features, see the examples directory on GitHub. There are step-by-step walkthroughs for querying the NWS Weather API, PokéAPI, CarAPI, GitHub, and Threads.
Basic Query
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Nested Resources
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
POST-for-Read
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Custom Headers
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
API Key Location
By default, the API key is sent as a header. Use api_key_location to send it as a query parameter or cookie instead:
1 2 3 4 5 6 7 8 9 10 11 | |
Response Path Extraction
For APIs that wrap data in a container object:
1 2 3 4 5 6 7 8 9 10 11 | |
Combining with Materialized Views
For frequently accessed data, use materialized views to reduce API calls:
1 2 3 4 5 6 7 8 | |