Heratio Help Center article. Category: Integration.

GraphQL API User Guide

The GraphQL endpoint lets a signed-in user ask for exactly the fields they need in a single query.

Read-only, staff session only. GraphQL in Heratio answers queries; it has no mutations, and it is not reachable with an API key. To create, update or delete records from a program, use the REST API v2, which has full CRUD with API keys (see the API user guide and docs/api-worked-examples.md). heratio#1539.

Overview

Feature Description
Endpoint POST /admin/graphql/execute
Authentication Signed-in browser session (read permission); no API keys
Operations Queries only (read-only)
Format JSON
Playground /admin/graphql/playground

Quick Start

1. Sign in

Sign in to Heratio in the browser. The playground at /admin/graphql/playground uses that session, so it is the easiest place to try queries.

2. Make Your First Query

From the playground, or from a script that carries the browser session cookie and CSRF token:

curl -X POST https://your-instance.com/admin/graphql/execute \
  -H "Content-Type: application/json" \
  -H "X-CSRF-TOKEN: <token from the page>" \
  -b "laravel_session=<your session cookie>" \
  -d '{"query": "{ items(first: 5) { totalCount edges { node { title slug } } } }"}'

3. Response

{
  "data": {
    "items": {
      "totalCount": 190,
      "edges": [
        { "node": { "title": "Annual Reports", "slug": "annual-reports" } },
        { "node": { "title": "Correspondence", "slug": "correspondence" } }
      ]
    }
  }
}

Authentication

GraphQL uses the signed-in session only. An API key in X-API-Key or Authorization: Bearer is not accepted here; programs that authenticate with a key should use the REST API.

Core Queries

Browse Items (Archival Descriptions)

query BrowseItems {
  items(first: 10, repository: "main-archive", level: "fonds") {
    totalCount
    edges {
      node {
        id
        slug
        title
        levelOfDescription { name }
        sector
        repository { name }
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Arguments:

  • first - Number of items (max 100)
  • after - Cursor for pagination
  • repository - Filter by repository slug
  • level - Filter by level (fonds, series, file, item)
  • sector - Filter by sector (archive, library, museum)

Get Single Item

query GetItem {
  item(slug: "annual-reports") {
    id
    slug
    identifier
    title
    scopeAndContent
    extentAndMedium
    accessConditions

    levelOfDescription { name }
    repository { name slug }

    dates {
      eventType
      dateDisplay
      startDate
      endDate
    }

    subjects { name }
    places { name }
    creators {
      slug
      authorizedFormOfName
    }

    digitalObjects {
      name
      mimeType
      thumbnailUrl
      masterUrl
    }

    parent { title slug }
    children(first: 5) {
      totalCount
      edges { node { title slug } }
    }
    ancestors { title slug }
  }
}

Browse Actors (Authority Records)

query BrowseActors {
  actors(first: 10, entityType: "person") {
    totalCount
    edges {
      node {
        id
        slug
        authorizedFormOfName
        entityType { name }
        datesOfExistence
        history
      }
    }
  }
}

Get Single Actor

query GetActor {
  actor(slug: "john-smith") {
    id
    slug
    authorizedFormOfName
    entityType { name }
    datesOfExistence
    history
    places
    functions

    relatedItems(first: 10) {
      totalCount
      edges {
        node { title slug }
      }
    }
  }
}

Browse Repositories

query BrowseRepositories {
  repositories(first: 10) {
    totalCount
    edges {
      node {
        id
        slug
        name
        identifier
        itemCount

        holdings(first: 5) {
          totalCount
          edges { node { title } }
        }
      }
    }
  }
}

Get Taxonomies

query GetTaxonomies {
  taxonomies {
    id
    name
    usage
    terms {
      id
      name
      code
      parent { name }
      children { name }
    }
  }
}
query Search {
  search(query: "correspondence 1950", first: 20) {
    totalCount
    edges {
      node {
        title
        slug
        identifier
        levelOfDescription { name }
        repository { name }
      }
    }
  }
}

Current User

query Me {
  me {
    id
    username
    email  # Only visible to admins or self
  }
}

Pagination

GraphQL uses cursor-based pagination (Relay-style connections):

# First page
query FirstPage {
  items(first: 10) {
    edges { node { title } cursor }
    pageInfo { hasNextPage endCursor }
  }
}

# Next page (use endCursor from previous response)
query NextPage {
  items(first: 10, after: "Y3Vyc29yOjEw") {
    edges { node { title } cursor }
    pageInfo { hasNextPage endCursor }
  }
}

Variables

Use variables for dynamic queries:

query GetItem($slug: String!) {
  item(slug: $slug) {
    title
    scopeAndContent
  }
}

Request:

{
  "query": "query GetItem($slug: String!) { item(slug: $slug) { title } }",
  "variables": { "slug": "annual-reports" }
}

Fragments

Reuse field selections:

fragment ItemBasics on Item {
  id
  slug
  title
  levelOfDescription { name }
}

query {
  items(first: 5) {
    edges {
      node {
        ...ItemBasics
        repository { name }
      }
    }
  }
}

Error Handling

Errors are returned in the errors array:

{
  "errors": [
    {
      "message": "Query depth of 12 exceeds maximum allowed depth of 10",
      "extensions": { "code": "DEPTH_LIMIT" }
    }
  ]
}

Common errors:

Error Cause Solution
Unauthorized Not signed in, or the session expired Sign in again in the browser
Depth limit exceeded Query too deep (>10 levels) Reduce nesting
Complexity exceeded Query too expensive (>1000) Reduce fields/pagination

Security Limits

Limit Value Purpose
Max Depth 10 levels Prevent deeply nested queries
Max Complexity 1000 Prevent expensive queries
Max Results 100 per page Pagination limit
Introspection Dev only Schema hidden in production

GraphQL Playground

In development mode, access the interactive GraphQL Playground at:

https://your-instance.com/admin/graphql/playground

Features:

  • Schema documentation
  • Query autocompletion
  • Query history
  • Response formatting

Comparison: GraphQL vs REST

Use Case REST GraphQL
Get item with children 2+ requests 1 request
Get only titles Returns all fields Returns only titles
Browse with relations Multiple endpoints Single query
Mobile/bandwidth More data transfer Minimal data

GraphQL Advantages:

  • Request exactly what you need
  • Single request for related data
  • Self-documenting schema
  • Strongly typed

REST Advantages:

  • Simpler for basic CRUD
  • HTTP caching
  • More familiar to most developers

Example: Complete Item View

query CompleteItem($slug: String!) {
  item(slug: $slug) {
    # Identity
    id
    slug
    identifier
    title

    # Classification
    levelOfDescription { name }
    sector

    # Content
    scopeAndContent
    extentAndMedium
    archivalHistory
    acquisition
    arrangement

    # Access
    accessConditions
    reproductionConditions

    # Context
    repository {
      name
      slug
    }

    # Dates
    dates {
      eventType
      dateDisplay
      startDate
      endDate
    }

    # Access Points
    subjects { id name }
    places { id name }
    creators {
      slug
      authorizedFormOfName
      entityType { name }
    }

    # Digital Objects
    digitalObjects {
      id
      name
      mimeType
      byteSize
      thumbnailUrl
      masterUrl
    }

    # Hierarchy
    parent { title slug }
    ancestors { title slug }
    children(first: 20) {
      totalCount
      edges {
        node {
          title
          slug
          levelOfDescription { name }
        }
      }
    }
    childrenCount
  }
}

Support