GraphQL ​
AYB exposes a GraphQL API backed by live database schema metadata.
Endpoint ​
- HTTP query/mutation:
POST /api/graphql - WebSocket subscriptions (
graphql-transport-ws):GET /api/graphqlwith WebSocket upgrade
GET /api/graphql without WebSocket upgrade is rejected with 405 Method Not Allowed (websocket upgrade required for GET /graphql).
Authentication ​
When auth is enabled:
POST /api/graphqlis mounted with the same admin-or-user auth middleware used by REST API routes.- WebSocket auth is enforced in the
graphql-transport-wsprotocol init step (connection_init) via bearer token or API key validation. - Invalid/missing auth during init closes the socket with close code
4401.
curl -X POST http://localhost:8090/api/graphql \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"query":"{ posts(limit: 5) { id title } }"}'Tutorial: table to JavaScript SDK ​
This walkthrough starts from a new table, inserts deterministic rows through POST /api/graphql, queries a hand-calculated result, then runs the same query through the JavaScript SDK GraphQL client.
Enable GraphQL before starting the server:
[graphql]
enabled = trueexport AYB_BASE_URL="http://127.0.0.1:8090"
ayb sql "DROP TABLE IF EXISTS docs_graphql_users;
CREATE TABLE docs_graphql_users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
tier TEXT NOT NULL,
score INTEGER NOT NULL
)"Insert two known rows with a parameterized GraphQL mutation:
curl -sS -X POST "$AYB_BASE_URL/api/graphql" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation InsertTutorialUsers($objects: [DocsGraphqlUsersInsertInput!]!) { insert_docs_graphql_users(objects: $objects) { affected_rows returning { id name tier score } } }",
"variables": {
"objects": [
{ "id": 1, "name": "Ada Lovelace", "tier": "gold", "score": 98 },
{ "id": 2, "name": "Grace Hopper", "tier": "silver", "score": 91 }
]
}
}'Expected response:
{
"data": {
"insert_docs_graphql_users": {
"affected_rows": 2,
"returning": [
{ "id": 1, "name": "Ada Lovelace", "tier": "gold", "score": 98 },
{ "id": 2, "name": "Grace Hopper", "tier": "silver", "score": 91 }
]
}
}
}Query only the gold-tier row:
curl -sS -X POST "$AYB_BASE_URL/api/graphql" \
-H "Content-Type: application/json" \
-d '{
"query": "query TutorialUsers($tier: String!) { docs_graphql_users(where: { tier: { _eq: $tier } }, order_by: { id: ASC }) { id name tier score } }",
"variables": { "tier": "gold" }
}'Expected response:
{
"data": {
"docs_graphql_users": [
{ "id": 1, "name": "Ada Lovelace", "tier": "gold", "score": 98 }
]
}
}Run the same query through the JavaScript SDK:
import { AYBClient } from "@allyourbase/js";
const ayb = new AYBClient(process.env.AYB_BASE_URL || "http://127.0.0.1:8090");
const data = await ayb.graphql.query(`
query TutorialUsers($tier: String!) {
docs_graphql_users(
where: { tier: { _eq: $tier } }
order_by: { id: ASC }
) {
id
name
tier
score
}
}
`, { tier: "gold" });
console.log(JSON.stringify(data.docs_graphql_users));Expected output:
[{"id":1,"name":"Ada Lovelace","tier":"gold","score":98}]Clean up the tutorial table:
ayb sql "DROP TABLE IF EXISTS docs_graphql_users"Query example ​
Table names become root fields.
query Posts {
posts(
where: { published: { _eq: true } }
order_by: { created_at: DESC }
limit: 20
offset: 0
) {
id
title
published
}
}Mutation examples ​
For table posts, AYB generates:
insert_postsupdate_postsdelete_posts
mutation CreatePost {
insert_posts(objects: [{ title: "Hello", published: true }]) {
affected_rows
returning {
id
title
}
}
}mutation UpdatePost {
update_posts(
where: { id: { _eq: 42 } }
_set: { title: "Updated" }
) {
affected_rows
returning {
id
title
}
}
}mutation DeletePost {
delete_posts(where: { id: { _eq: 42 } }) {
affected_rows
}
}Subscriptions ​
Subscriptions are table-based and stream row changes.
subscription WatchPosts {
posts(where: { published: { _eq: true } }) {
id
title
published
}
}Use graphql-transport-ws protocol on ws://localhost:8090/api/graphql.
Transport behavior:
- Subprotocol
graphql-transport-wsis required. connection_initmust be sent beforesubscribe.- Sending
subscribebeforeconnection_initcloses with4401.
Schema introspection ​
query Introspect {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
}
}Introspection behavior is controlled by graphql.introspection config:
""(default): admin-gated"open": no introspection gate"disabled": blocked
Error envelope differences vs REST ​
- GraphQL request/validation/execution errors are returned as GraphQL errors (
{ "errors": [...] }), commonly with HTTP200. - Malformed JSON body returns HTTP
400with GraphQL-style error envelope. - Introspection blocked by policy returns HTTP
403with GraphQL-style error envelope. - REST endpoints use
{ code, message, data?, doc_url? }via sharedhttputilhelpers.
Practical notes ​
- Tables prefixed with
_ayb_are excluded. - Views/materialized views are queryable but not mutation targets.
limitis capped server-side (default max 1000 rows).- Filters support
_eq,_neq,_gt,_gte,_lt,_lte,_in,_like,_ilike,_is_null.