GraphQL
Query your Plexivo Postgres tables with GraphQL, including live queries that update as data changes.
Every Plexivo Postgres with the Data API on is also a GraphQL API. The schema is built from the tables and functions your Data API exposes, so it follows your database as it changes, and row-level security applies to every field.
The GraphQL endpoint#
Send queries to your Project URL followed by /graphql/v1, with the same keys and user tokens as the Data API. POST takes one query or a batch of up to 20; GET runs queries only.
curl "$PLEXIVO_DATA_URL/graphql/v1" \
-H "apikey: $PLEXIVO_PUBLISHABLE_KEY" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ orders(limit: 5) { id total } }"}'Try queries first in the GraphQL tab’s Playground, As a visitor or As the service role, and download the schema as a .graphql file from Schema.
The schema#
For a table orders, you get:
| Field | What it does |
|---|---|
orders(where, orderBy, limit, offset) | Reads rows |
ordersByPk(id) | Reads one row by its primary key |
ordersAggregate(where) | Counts and aggregates |
insertOrders, insertOrdersOne | Inserts, with onConflict for an upsert |
updateOrders, updateOrdersByPk | Updates, with set and inc |
deleteOrders, deleteOrdersByPk | Deletes |
- Each column is a field of the same name. Each foreign key is a field both ways:
customeron an order forcustomer_id, andorderson a customer, with its ownwhere,orderByandlimit. - Filters:
eq,neq,gt,gte,lt,lte,in,nin,isNull,like,ilike,regex,iregex;containsandcontainedInfor arrays and JSON, andoverlapsfor arrays;searchon full-text (tsvector) columns; andand,orandnot. - Your exposed SQL functions appear as queries when they are
stableorimmutable, and as mutations otherwise. PostgreSQL enums become GraphQL enums.
Queries and mutations#
query OpenOrders {
orders(where: { status: { eq: "open" } }, orderBy: [{ created_at: DESC }], limit: 20) {
id
total
customer { name }
}
ordersAggregate(where: { status: { eq: "open" } }) { count }
}
mutation Pay($id: BigInt!) {
updateOrdersByPk(id: $id, set: { status: "paid" }) { id status }
}All the mutations in one request commit together or not at all. A bigint column is a BigInt, sent as a string so no digit is lost; numeric is a BigFloat, and timestamps are Datetime.
const { data, errors } = await db.graphql(
'query($s: String!) { orders(where: { status: { eq: $s } }) { id total } }',
{ s: 'open' }
);final result = await db.graphql(
r'query($s: String!) { orders(where: { status: { eq: $s } }) { id total } }',
variables: {'s': 'open'},
);Send { persisted: true } in JavaScript, or persisted: true in Dart, to use automatic persisted queries: after the first time, only the query’s hash travels.
Live queries#
A live query sends its answer now, and again whenever a row it reads changes. Every table it reads must have Realtime switched on.
const live = db.graphql.subscribe(
'subscription { orders(where: { status: { eq: "open" } }) { id total } }',
{},
({ data }) => render(data.orders)
);
live.unsubscribe();final stop = await db.graphqlSubscribe(
'subscription { orders(where: { status: { eq: "open" } }) { id total } }',
(result) => render(result['data']),
);
await stop();Changes that arrive together are sent as one update. One live-query channel holds up to 20 live queries; the SDKs open a channel for each live query.
Security#
GraphQL runs every field as the caller, exactly like the Data API: tables and functions not exposed there are not in the schema, and row-level security decides which rows each user sees and writes.
Limits#
- Queries nest up to 12 levels and select up to 500 fields.
- A query document is up to 100,000 bytes.
- A list returns up to 1,000 rows.
Next steps#
Updated 27 October 2026