Skip to main content
The GraphQL API is the primary interface for interacting with Infrahub. It provides a flexible, type-safe way to query and mutate infrastructure data with support for branching, time-travel queries, and real-time subscriptions.

Endpoint

URL Pattern: /graphql/{branch_name} Examples:
  • /graphql/main - Main branch
  • /graphql/feature-network-updates - Feature branch

Making Requests

HTTP POST

Send GraphQL queries via HTTP POST with JSON body:
curl -X POST http://localhost:8000/graphql/main \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query { InfraDevice { edges { node { id name { value } } } } }"
  }'

With Variables

curl -X POST http://localhost:8000/graphql/main \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query GetDevice($name: String!) { InfraDevice(name__value: $name) { edges { node { id name { value } } } } }",
    "variables": { "name": "router-01" }
  }'

With Operation Name

curl -X POST http://localhost:8000/graphql/main \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query GetDevices { InfraDevice { edges { node { id } } } } query GetSites { InfraSite { edges { node { id } } } }",
    "operationName": "GetDevices"
  }'

Time-Travel Queries

Query historical data using the at query parameter:
# Absolute timestamp
curl -X POST "http://localhost:8000/graphql/main?at=2024-01-15T10:30:00Z" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { InfraDevice { edges { node { id name { value } } } } }"}'

# Relative time (-1 hour)
curl -X POST "http://localhost:8000/graphql/main?at=-1h" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { InfraDevice { edges { node { id name { value } } } } }"}'
Supported relative time formats:
  • -30m - 30 minutes ago
  • -1h - 1 hour ago
  • -7d - 7 days ago
Note: Mutations are not supported with the at parameter. Infrahub automatically resets at to the current time for mutation operations.

Branch Operations

Query from a Branch

Specify the branch in the URL path:
curl -X POST http://localhost:8000/graphql/feature-branch \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"query": "query { InfraDevice { edges { node { id name { value } } } } }"}'

Mutations on Branches

All mutations automatically target the branch specified in the URL:
curl -X POST http://localhost:8000/graphql/feature-branch \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { InfraDeviceCreate(data: { name: { value: \"router-02\" } }) { ok object { id } } }"
  }'

Real-Time Subscriptions

GraphQL subscriptions enable real-time updates over WebSocket connections.

WebSocket Connection

URL Pattern: ws://localhost:8000/graphql/{branch_name} Subscriptions use the graphql-ws protocol:
const ws = new WebSocket('ws://localhost:8000/graphql/main', 'graphql-ws');

ws.onopen = () => {
  // Initialize connection
  ws.send(JSON.stringify({ type: 'connection_init' }));
  
  // Start subscription
  ws.send(JSON.stringify({
    id: '1',
    type: 'start',
    payload: {
      query: 'subscription { event { event_type node_id } }'
    }
  }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('Received:', message);
};

Subscription Protocol Messages

Client → Server:
  • connection_init - Initialize connection
  • start - Start subscription
  • stop - Stop subscription
  • connection_terminate - Close connection
Server → Client:
  • connection_ack - Connection acknowledged
  • data - Subscription data
  • error - Error occurred
  • complete - Subscription completed

Schema Introspection

Get Schema

Query the GraphQL schema using introspection:
query IntrospectionQuery {
  __schema {
    types {
      name
      kind
      description
      fields {
        name
        description
        type {
          name
          kind
        }
      }
    }
  }
}

Get Type Information

query {
  __type(name: "InfraDevice") {
    name
    kind
    description
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

Common Query Patterns

List All Objects

query {
  InfraDevice {
    edges {
      node {
        id
        name {
          value
        }
      }
    }
  }
}

Filter by Attribute

query {
  InfraDevice(name__value: "router-01") {
    edges {
      node {
        id
        name {
          value
        }
      }
    }
  }
}

Nested Relationships

query {
  InfraDevice {
    edges {
      node {
        id
        name {
          value
        }
        site {
          node {
            id
            name {
              value
            }
          }
        }
      }
    }
  }
}

Pagination

query {
  InfraDevice(first: 10, after: "cursor") {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      cursor
      node {
        id
        name {
          value
        }
      }
    }
  }
}

Common Mutations

Create Object

mutation {
  InfraDeviceCreate(
    data: {
      name: { value: "router-01" }
      description: { value: "Core router" }
    }
  ) {
    ok
    object {
      id
      name {
        value
      }
    }
  }
}

Update Object

mutation {
  InfraDeviceUpdate(
    data: {
      id: "<device-id>"
      name: { value: "router-01-updated" }
    }
  ) {
    ok
    object {
      id
      name {
        value
      }
    }
  }
}

Delete Object

mutation {
  InfraDeviceDelete(data: { id: "<device-id>" }) {
    ok
  }
}

Upsert Object

mutation {
  InfraDeviceUpsert(
    data: {
      name: { value: "router-01" }
      description: { value: "Core router" }
    }
  ) {
    ok
    object {
      id
    }
  }
}

Response Format

Successful Query

{
  "data": {
    "InfraDevice": {
      "edges": [
        {
          "node": {
            "id": "abc123",
            "name": {
              "value": "router-01"
            }
          }
        }
      ]
    }
  }
}

Query with Errors

{
  "data": null,
  "errors": [
    {
      "message": "Node not found",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": ["InfraDevice"],
      "extensions": {
        "code": "NODE_NOT_FOUND"
      }
    }
  ]
}

Partial Success

{
  "data": {
    "InfraDevice": {
      "edges": []
    }
  },
  "errors": [
    {
      "message": "Permission denied for field 'password'",
      "path": ["InfraDevice", 0, "password"]
    }
  ]
}

Performance Optimization

Query Depth and Height

Infrahub tracks query complexity metrics:
  • Depth: Maximum nesting level of the query
  • Height: Total number of fields requested
See /home/daytona/workspace/source/backend/infrahub/graphql/app.py:281 for metric implementation.

Field Selection

Only request fields you need:
# Good - Only request needed fields
query {
  InfraDevice {
    edges {
      node {
        id
        name { value }
      }
    }
  }
}

# Avoid - Requesting all fields
query {
  InfraDevice {
    edges {
      node {
        id
        name { value }
        description { value }
        # ... all other fields
      }
    }
  }
}

Use Pagination

Always use pagination for large datasets:
query {
  InfraDevice(first: 50) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        name { value }
      }
    }
  }
}

Metrics and Monitoring

Infrahub exposes Prometheus metrics for GraphQL operations:
  • graphql_duration - Query execution time
  • graphql_query_depth - Query depth distribution
  • graphql_query_height - Query height distribution
  • graphql_query_errors - Error count
  • graphql_response_size - Response size in bytes
  • graphql_query_objects - Number of impacted models
See /home/daytona/workspace/source/backend/infrahub/graphql/metrics.py for implementation.

Error Codes

Common GraphQL error codes:
  • NODE_NOT_FOUND - Requested node doesn’t exist
  • PERMISSION_DENIED - Insufficient permissions
  • VALIDATION_ERROR - Input validation failed
  • CONSTRAINT_VIOLATION - Uniqueness or other constraint violated
  • BRANCH_NOT_FOUND - Specified branch doesn’t exist

Build docs developers (and LLMs) love