Auto-generated GraphQL schema from your SQL tables. Queries, mutations, and live subscriptions over WebSocket — compatible with Apollo, Relay, and any standard GraphQL client.
Absolute DB exposes a GraphQL endpoint alongside its REST API on port 8080. The GraphQL schema is automatically derived from the database schema — every table becomes a queryable type, every column becomes a field, and foreign key relationships become nested object resolvers.
There is nothing to configure. Create a table in SQL and it is immediately queryable via GraphQL. Schema changes (ALTER TABLE) are reflected in the GraphQL schema within milliseconds.
| Property | Value |
|---|---|
| Endpoint | http://host:8080/graphql |
| WebSocket subscriptions | ws://host:8080/graphql |
| Introspection | Enabled by default (disable with --graphql-no-introspection) |
| Schema source | Auto-generated from SQL table definitions |
| Compatible clients | Apollo Client, Relay, urql, graphql-request, any GraphQL client |
| Subscription protocol | graphql-ws over WebSocket (RFC 6455) |
# Check the endpoint is alive
curl http://localhost:8080/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{ __typename }"}'
# Fetch full introspection schema
curl http://localhost:8080/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{ __schema { types { name } } }"}'
# Using graphql-introspect tool
npx get-graphql-schema http://localhost:8080/graphql > schema.graphql
The GraphQL playground is also accessible at http://localhost:8080/graphql from a browser, providing an interactive IDE with auto-complete and schema documentation.
Given a SQL table definition, Absolute DB generates the corresponding GraphQL types automatically:
CREATE TABLE products (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL,
category TEXT,
in_stock BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT now()
);
type Product {
id: ID!
name: String!
price: Float!
category: String
inStock: Boolean
createdAt: String
}
type Query {
product(id: ID!): Product
products(
where: ProductFilter
orderBy: ProductOrderBy
limit: Int
offset: Int
): [Product!]!
}
type Mutation {
insertProduct(input: ProductInput!): Product!
updateProduct(id: ID!, input: ProductInput!): Product
deleteProduct(id: ID!): Boolean!
}
type Subscription {
productChanged(where: ProductFilter): ProductChangeEvent!
}
Column names are automatically converted from snake_case to camelCase in the GraphQL schema. SQL NOT NULL constraints map to non-nullable GraphQL fields (!).
query GetProduct {
product(id: "42") {
id
name
price
category
inStock
}
}
query ListProducts {
products(
where: { category: { eq: "electronics" }, inStock: { eq: true } }
orderBy: { price: ASC }
limit: 20
offset: 0
) {
id
name
price
}
}
query OrderWithCustomer {
order(id: "101") {
id
total
status
customer {
id
name
email
}
items {
product { name price }
quantity
}
}
}
mutation CreateProduct {
insertProduct(input: {
name: "Wireless Keyboard"
price: 79.99
category: "electronics"
inStock: true
}) {
id
name
createdAt
}
}
mutation UpdatePrice {
updateProduct(id: "42", input: { price: 69.99 }) {
id
name
price
}
}
mutation RemoveProduct {
deleteProduct(id: "42")
}
All mutations are fully transactional. An error during a mutation results in a complete rollback — no partial writes. RETURNING semantics from the underlying SQL are exposed as the mutation's return type.
GraphQL subscriptions use the graphql-ws protocol over a WebSocket connection to ws://host:8080/graphql. The server pushes change events to subscribers as rows are inserted, updated, or deleted — powered by the same WAL-tap CDC engine used by the gRPC Subscribe service.
subscription WatchOrders {
orderChanged(where: { status: { eq: "pending" } }) {
operation # INSERT | UPDATE | DELETE
record {
id
total
status
customerId
}
}
}
import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { createClient } from 'graphql-ws';
import { getMainDefinition } from '@apollo/client/utilities';
const httpLink = new HttpLink({ uri: 'http://localhost:8080/graphql' });
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:8080/graphql',
}));
const splitLink = split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
},
wsLink,
httpLink,
);
const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache() });
Every generated query type includes a where argument with per-field filter operators:
| Operator | GraphQL | SQL Equivalent |
|---|---|---|
| Equal | { eq: value } | col = value |
| Not equal | { ne: value } | col != value |
| Greater than | { gt: value } | col > value |
| Less than | { lt: value } | col < value |
| In list | { in: [v1, v2] } | col IN (v1, v2) |
| Like | { like: "prefix%" } | col LIKE 'prefix%' |
| Is null | { isNull: true } | col IS NULL |
Pagination uses limit and offset arguments. Cursor-based pagination is also supported via the after and before arguments using opaque base64 cursors.
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
const client = new ApolloClient({
link: new HttpLink({ uri: 'http://localhost:8080/graphql' }),
cache: new InMemoryCache(),
});
// Query
const { data } = await client.query({
query: gql`
query {
products(limit: 10, orderBy: { createdAt: DESC }) {
id name price
}
}
`,
});
// Mutation
await client.mutate({
mutation: gql`
mutation($name: String!, $price: Float!) {
insertProduct(input: { name: $name, price: $price }) { id }
}
`,
variables: { name: "New Widget", price: 29.99 },
});
import requests
ENDPOINT = 'http://localhost:8080/graphql'
def gql(query, variables=None):
resp = requests.post(ENDPOINT, json={
'query': query,
'variables': variables or {},
})
resp.raise_for_status()
data = resp.json()
if 'errors' in data:
raise RuntimeError(data['errors'])
return data['data']
# Fetch products
result = gql("""
query ListProducts($cat: String) {
products(where: { category: { eq: $cat } }, limit: 5) {
id name price
}
}
""", variables={'cat': 'electronics'})
for p in result['products']:
print(p['name'], p['price'])
# Insert a product
new = gql("""
mutation($input: ProductInput!) {
insertProduct(input: $input) { id name }
}
""", variables={'input': {'name': 'USB Hub', 'price': 24.99}})
All GraphQL responses follow the GraphQL specification JSON format:
{
"data": {
"products": [
{ "id": "1", "name": "Wireless Keyboard", "price": 79.99 },
{ "id": "2", "name": "USB Hub", "price": 24.99 }
]
}
}
{
"data": null,
"errors": [
{
"message": "Table 'prodcts' does not exist",
"locations": [{ "line": 2, "column": 3 }],
"path": ["prodcts"],
"extensions": { "code": "NOT_FOUND" }
}
]
}
Partial success is supported — if a query returns multiple top-level fields and one fails, the successful fields are returned in data and the error appears in errors.
For cases where the auto-generated schema is insufficient, Absolute DB exposes a raw SQL escape hatch through the sql query field:
query AdvancedReport {
sql(statement: """
SELECT
date_trunc('month', created_at) AS month,
category,
sum(price) AS revenue
FROM orders
JOIN order_items USING (order_id)
JOIN products USING (product_id)
WHERE created_at >= '2026-01-01'
GROUP BY 1, 2
ORDER BY 1, 3 DESC
""") {
columns
rows
}
}
The sql field requires the EXECUTE_RAW_SQL privilege. Bind parameters are supported to prevent injection: pass params: ["value1", "value2"] and use $1, $2 in the statement.
Pass authentication credentials in the HTTP Authorization header for all GraphQL requests:
| Method | Header Value |
|---|---|
| Basic (username + password) | Basic base64(username:password) |
| JWT / OIDC (Auth0, Okta, Azure AD) | Bearer <jwt_token> |
Row-level security policies defined in SQL are enforced automatically for every GraphQL query and mutation. A user can only read or write rows their role permits — no additional GraphQL-layer access control is required.
TOKEN=$(curl -s -X POST https://auth.example.com/oauth/token \
-d '{"client_id":"...","grant_type":"client_credentials"}' \
| jq -r '.access_token')
curl http://localhost:8080/graphql \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"query":"{ products(limit:5){ id name } }"}'
~154 KB binary · zero external dependencies · 2,737 tests passing · SQL:2023 100%