Integrations connect a site to an outside service once. Queries use an integration to fetch data on a page, and bindings read the result as queries.name.data.
Integrations
Add one in the Data workspace. Fill in the connection settings, enter the secrets (tokens, passwords, API keys), save, and press Run test under Test connection. Admins create and edit integrations; editors can use them in queries and test them.
What you need
The form labels come from the setting names, so they read like Base url or Ssl.
- REST: the Base url, and for auth a Bearer token, a basic auth Username and Password, or an API key with its header or query parameter name.
- GraphQL: the Endpoint, and a Bearer token or an API key if it needs auth.
- PostgreSQL: Host, Port, Database, User and Password, from your provider’s connection details (in Neon, Connect on the project dashboard; in Supabase, Connect at the top of the project).
- Supabase: the Project url and an API key from the project’s API settings. The anon key respects row-level security; the service-role key bypasses it, so use it only for server workflows.
- Airtable: the Base id (the
app…part of the base URL) and a Personal access token withdata.records:read(anddata.records:writeto change records) on that base. - Google Sheets: the Spreadsheet id from the sheet URL, and a Service account key (JSON) with the sheet shared with the service account email. An API key is enough to read sheets that are public on the web.
- Resend: a Default from sender on a domain verified in Resend, and an API key; a sending-access key is enough.
- Claude: an API key from the Claude Console.
- OpenAI: an API key from the OpenAI platform. Base url can point at any OpenAI-compatible API.
PostgreSQL
Fill in the host, port, database and user, and the password as a secret. Use a role with only the rights the site needs. Write SQL with $1, $2… placeholders and pass the values in params; values are never pasted into the SQL. Query runs in a read-only transaction, so it cannot change data; Execute can write. Results come back as { rows, rowCount, columns, truncated }: set the response path to rows for a collection list.
Create a query
Pick the integration and operation
For example REST Request, GraphQL Query or CMS List items.
Fill in the input
Any input value can be an expression. Read variables to make the query react:
vars.search,page.params.slug.Choose when it runs
Pick a mode (below). Run it once in the editor to see the result and get autocomplete on its fields.
Narrow the result
Set a response path such as
data.itemssoqueries.products.datais the array you want.
{
"name": "products",
"integrationId": "int_shop_api",
"operation": "rest.request",
"input": {
"method": "GET",
"path": "/products",
"query": { "search": { "$expr": "vars.search" } }
},
"responsePath": "data.items",
"run": { "mode": "client", "on": "load", "debounceMs": 250 }
}
Static, server or client
| Mode | Runs | Use it for |
|---|---|---|
| Static | at publish; the result is baked into the HTML | marketing pages, CMS content, best SEO |
| Server | per request at the edge, optionally cached for N seconds | fresh or personalized data that must still be indexed |
| Client | in the browser, on load and whenever its inputs change (debounced), or only from workflows | search, filters, dashboards |
Reading a query
queries.name.data: the result.queries.name.loading: true while it runs. Bind a spinner’s visibility to it.queries.name.error:{ message, code }or null.queries.name.updatedAt: when the last result arrived.
Refetch a client query from a workflow with Refetch query. To run an operation that writes or costs money from a page, use Run server workflow with a server workflow called from pages. Lists show query results with collection lists.