GraphQL API
Arranger exposes a single GraphQL endpoint per server — the primary programmatic interface for searching a catalogue. A client combines a SQON filter with field selections, pagination, and sorting; Arranger translates the whole request into an Elasticsearch query and returns the results (see Query Processing for the end-to-end flow).
The schema is generated per catalogue from the catalogue's index mapping and configuration — there is no hand-written schema, so field names and types vary by catalogue. Discover them at runtime with the Introspection API.
Endpoint
POST /graphql
A running server serves GraphQL at /graphql (a local development server, for example, at http://localhost:5050/graphql). Requests are standard GraphQL over HTTP: a JSON body with a query string and an optional variables object. The paths for a given server — including the per-catalogue GraphQL paths used in multi-catalogue mode — are listed by GET /introspection (see the Introspection API).
Schema shape
For each catalogue, the root query exposes a field named after the catalogue's document type (for example file or participant, set in the catalogue configuration). That type carries:
| Field | Purpose |
|---|---|
hits | The matching records, as a paginated connection. |
aggregations | Per-field facet buckets — each a value and its document count — over the filtered result set. |
configs | The catalogue's table, facet, and display configuration. |
mapping | The raw Elasticsearch mapping, as JSON. |
hits and aggregations both accept a filters argument that takes a SQON.
Querying records: hits
query SearchFiles($sqon: JSON, $first: Int, $offset: Int, $sort: [Sort]) {
file {
hits(filters: $sqon, first: $first, offset: $offset, sort: $sort) {
total
edges {
node {
id
# the remaining fields come from the catalogue's index mapping —
# discover them with the Introspection API
}
}
}
}
}
with variables:
{
"sqon": {
"op": "and",
"content": [
{ "op": "in", "content": { "fieldName": "data.primary_site", "value": ["Brain"] } }
]
},
"first": 20,
"offset": 0,
"sort": [{ "fieldName": "data.primary_site", "order": "asc" }]
}
hits arguments:
| Argument | Type | Purpose |
|---|---|---|
filters | JSON (SQON) | The filter to apply; omit to match all records. |
first | Int | Page size (defaults to 10 — see Defaults and Limits). |
offset | Int | Number of records to skip. |
sort | [Sort] | Ordering; each Sort is { fieldName, order, mode, missing }. |
searchAfter | JSON | Cursor for deep pagination, taken from a prior page's edges.searchAfter. |
trackTotalHits | Boolean | Whether total counts all matches (default true). |
hits result: a connection with total (the full match count, not just the current page) and edges, each holding a node. Every node has id and score; its remaining fields are those in the catalogue's index mapping.
Aggregations
aggregations returns, for each requested field, the distinct values (buckets) and their document counts in the filtered result set — the data behind a facet panel:
query Facets($sqon: JSON) {
file {
aggregations(filters: $sqon) {
data__primary_site {
bucket_count
buckets {
key
doc_count
}
}
}
}
}
Each field returns a bucket_count (the number of distinct values) and buckets, where each bucket's key is a value and doc_count its document count. See Concepts → Facets, buckets, and aggregations.
aggregations arguments: filters (a SQON), include_missing (also count documents missing the field), and aggregations_filter_themselves (whether a field's own facet selection constrains its buckets — set false for multi-select facet UIs).
GraphQL field names cannot contain dots, so a mapping field such as data.primary_site is exposed in the schema as data__primary_site. SQON fieldName values keep the dotted form (data.primary_site); GraphQL selections and aggregation names use the __ form. The Introspection API returns the dotted mapping names.
Discovering the schema
Because the schema is generated per catalogue, use the Introspection API to discover what you can query without writing a GraphQL query first:
GET /introspection/:catalogueId— every queryable field, its type, and the SQON operators it accepts.GET /introspection/sqon— the SQON JSON Schema shared across catalogues.
GraphQL's built-in introspection (__schema / __type) also works, but it is gated by the disableGraphQLIntrospection flag — disabled when NODE_ENV=production by default (see Introspection API → GraphQL introspection). Prefer the REST introspection endpoints for tooling, since they are always available.
Filtering with SQON
The filters argument on both hits and aggregations takes a SQON, Arranger's JSON filter language. Build it separately and pass it as a variable, as shown above. See Building SQON queries and SQON in detail.
If you encounter any issues or have questions, please don't hesitate to reach out through our support page or our discussion forum.