Documentation

GraphQL API

Auto-generated GraphQL schema from your SQL tables. Queries, mutations, and live subscriptions over WebSocket — compatible with Apollo, Relay, and any standard GraphQL client.

Overview

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.

PropertyValue
Endpointhttp://host:8080/graphql
WebSocket subscriptionsws://host:8080/graphql
IntrospectionEnabled by default (disable with --graphql-no-introspection)
Schema sourceAuto-generated from SQL table definitions
Compatible clientsApollo Client, Relay, urql, graphql-request, any GraphQL client
Subscription protocolgraphql-ws over WebSocket (RFC 6455)

Endpoint & Introspection

bash — Verify endpoint and fetch schema
# 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.

Auto-Generated Schema

Given a SQL table definition, Absolute DB generates the corresponding GraphQL types automatically:

sql — Create a table
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()
);
graphql — Auto-generated types
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 (!).

Queries

graphql — Fetch a single record by primary key
query GetProduct {
  product(id: "42") {
    id
    name
    price
    category
    inStock
  }
}
graphql — List with filter, sort, and pagination
query ListProducts {
  products(
    where: { category: { eq: "electronics" }, inStock: { eq: true } }
    orderBy: { price: ASC }
    limit: 20
    offset: 0
  ) {
    id
    name
    price
  }
}
graphql — Nested relationship (foreign key)
query OrderWithCustomer {
  order(id: "101") {
    id
    total
    status
    customer {
      id
      name
      email
    }
    items {
      product { name price }
      quantity
    }
  }
}

Mutations

graphql — Insert a record
mutation CreateProduct {
  insertProduct(input: {
    name:     "Wireless Keyboard"
    price:    79.99
    category: "electronics"
    inStock:  true
  }) {
    id
    name
    createdAt
  }
}
graphql — Update a record
mutation UpdatePrice {
  updateProduct(id: "42", input: { price: 69.99 }) {
    id
    name
    price
  }
}
graphql — Delete a record
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.

Subscriptions (Live Queries)

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.

graphql — Subscribe to live changes
subscription WatchOrders {
  orderChanged(where: { status: { eq: "pending" } }) {
    operation  # INSERT | UPDATE | DELETE
    record {
      id
      total
      status
      customerId
    }
  }
}
javascript — Apollo Client subscription
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() });

Filtering & Pagination

Every generated query type includes a where argument with per-field filter operators:

OperatorGraphQLSQL 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.

Apollo Client Setup

javascript — Minimal Apollo Client setup
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 },
});

Python Client

python — graphql-request equivalent using requests
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}})

JSON Response Format

All GraphQL responses follow the GraphQL specification JSON format:

json — Successful response
{
  "data": {
    "products": [
      { "id": "1", "name": "Wireless Keyboard", "price": 79.99 },
      { "id": "2", "name": "USB Hub",           "price": 24.99 }
    ]
  }
}
json — Error response
{
  "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.

Custom SQL Queries via GraphQL

For cases where the auto-generated schema is insufficient, Absolute DB exposes a raw SQL escape hatch through the sql query field:

graphql — Raw SQL execution via GraphQL
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.

Authentication

Pass authentication credentials in the HTTP Authorization header for all GraphQL requests:

MethodHeader 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.

bash — GraphQL request with JWT
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 } }"}'

Continue Reading

REST API gRPC API WebSocket / Live Queries

Ready to run Absolute DB?

~154 KB binary  ·  zero external dependencies  ·  2,737 tests passing  ·  SQL:2023 100%

Download Free → View Pricing All Docs