Skip to main content
Querying is how applications retrieve indexed blockchain data from Graph Node. Graph Node automatically generates a GraphQL API for each subgraph and translates queries into optimized SQL that executes against PostgreSQL.

Query Overview

Graph Node provides a rich GraphQL API for each deployed subgraph without requiring any additional code. The API is automatically generated from the subgraph’s schema and supports:
  • Filtering: Filter entities by any field
  • Sorting: Order results by any field
  • Pagination: Efficient cursor-based and offset-based pagination
  • Nested queries: Load related entities in a single query
  • Time-travel: Query historical state at any block
  • Full-text search: Search text fields with ranking
  • Aggregations: Compute sums, counts, averages, etc.
All queries are read-only. Graph Node does not support mutations via GraphQL - data is only modified by indexing blockchain events.

Query Endpoints

Subgraphs are queryable via HTTP POST to:

GraphQL Playground

Open the endpoint in a browser to access GraphiQL, an interactive query playground with:
  • Schema documentation
  • Autocomplete
  • Query validation
  • Result visualization

Query Structure

Queries follow standard GraphQL syntax and are automatically validated against the subgraph’s schema.

Basic Query

Nested Queries

Load related entities in a single request:

Query by ID

Fetch a specific entity by its ID:

Query Features

Filtering

Graph Node generates comprehensive filter arguments for every entity field:
Available for numeric, string, and byte fields:
  • field: Equals
  • field_not: Not equals
  • field_gt: Greater than
  • field_gte: Greater than or equal
  • field_lt: Less than
  • field_lte: Less than or equal
  • field_in: In array
  • field_not_in: Not in array
Additional operators for string fields:
  • field_contains: Contains substring
  • field_not_contains: Does not contain substring
  • field_starts_with: Starts with prefix
  • field_not_starts_with: Does not start with prefix
  • field_ends_with: Ends with suffix
  • field_not_ends_with: Does not end with suffix
Combine multiple conditions:
  • and: All conditions must match (implicit)
  • or: At least one condition must match
Implementation: store/postgres/src/relational_queries.rs translates to SQL WHERE clause
Filter by related entity fields using _ suffix:
SQL generation: Generates JOINs to related tables

Sorting

Sort results by any field:
Parameters:
  • orderBy: Field to sort by
  • orderDirection: asc (ascending) or desc (descending)
Multi-field sorting: Not yet supported in API, but planned

Pagination

Graph Node supports two pagination strategies:
Use first and skip for offset-based pagination:
Limitation: Large offsets are inefficient (must scan all skipped rows)Best for: Small datasets or low page numbers
Use first with where filter for efficient pagination:
Benefit: Consistent performance regardless of page depthBest for: Large datasets and deep pagination

Time-Travel Queries

Query historical state at any block number:
How it works: Block range filter in SQL:
This leverages the block range stored with each entity version. Location in codebase: store/postgres/src/block_range.rs Search text fields with relevance ranking (when enabled in schema):
Schema definition:
Implementation: Uses PostgreSQL full-text search features Reference: docs/aggregations.md

Aggregations

Compute aggregate values across entities:
Available aggregations:
  • count: Number of entities
  • sum: Sum of numeric fields
  • avg: Average of numeric fields
  • min: Minimum value
  • max: Maximum value
Reference: docs/aggregations.md

Meta Field

Query indexing metadata:
Use cases:
  • Check indexing progress
  • Verify data freshness
  • Detect indexing errors

Query Processing Pipeline

Graph Node translates GraphQL queries into SQL through several stages:

1. Parsing and Validation

The GraphQL query is parsed into an AST and validated:
Validation checks:
  • Query syntax is valid GraphQL
  • All fields exist in schema
  • Arguments are correctly typed
  • Required arguments are present
Location in codebase: graphql/src/execution/query.rs

2. Query Planning

The query planner determines optimal execution strategy:
  • Entity resolution: Map GraphQL types to database tables
  • Filter analysis: Identify indexes that can be used
  • Join planning: Optimize relationship traversals
  • Pagination strategy: Choose offset vs range-based pagination

3. SQL Generation

GraphQL query is translated to SQL:
The SQL generator:
  • Constructs SELECT with requested fields
  • Adds JOIN clauses for relationships
  • Applies WHERE filters
  • Adds ORDER BY and LIMIT clauses
  • Includes block range filters for time-travel
Example translation:
Generated SQL:
Location in codebase: store/postgres/src/relational_queries.rs

4. Query Execution

SQL executes against PostgreSQL:
Connection pooling: Each shard has a connection pool sized by configuration Read replicas: Queries are distributed across replicas based on weights Location in codebase: store/postgres/src/query_store.rs

5. Response Formatting

SQL results are transformed into GraphQL response:
  • Deserialization: PostgreSQL types → Rust types → GraphQL types
  • Relationship assembly: Join nested entities from separate queries
  • Type coercion: Apply GraphQL type rules
  • JSON formatting: Convert to GraphQL JSON response

Query Optimization

Indexing Strategy

PostgreSQL indexes are critical for query performance: Automatic indexes:
  • Primary key (id)
  • Version ID (vid)
  • Block range (block_range) using GiST index
Generated index example:
Location in codebase: store/postgres/src/relational.rs:generate_indexes()

Query Complexity Limits

Graph Node enforces limits to prevent resource exhaustion:
  • Max query depth: Limits nested query levels (default: 8)
  • Max result size: Limits returned entities (default: 1000 per query)
  • Query timeout: Maximum execution time (configurable)
Configuration:

Query Caching

GraphNode includes query result caching:
  • Cache key: Query hash + block number
  • Invalidation: Automatic on new blocks
  • Storage: In-memory cache per query node
Configuration:

Prepared Statements

Frequently-executed queries use prepared statements:
  • Query plan reuse: Avoids repeated planning overhead
  • Parameter binding: Safely inject filter values
  • Connection affinity: Same connection reuses prepared statement

Pagination Performance

Best practice: Use cursor-based pagination with indexed fields for large result sets. Avoid skip with large offsets.
Inefficient:
Efficient:

Query Performance Monitoring

Metrics

Graph Node exposes query metrics:
  • query_execution_time: Query duration histogram
  • query_validation_time: Validation time
  • query_cache_hit_rate: Cache effectiveness
  • query_parsing_errors: Failed query parsing
Endpoint: http://localhost:8040/metrics

Query Logging

Enable query logging for debugging:
Logs include:
  • Query text
  • Execution time
  • Number of results
  • SQL execution time

Slow Query Analysis

Identify slow queries:
Analyze with PostgreSQL:

GraphQL Schema Generation

Graph Node automatically generates a rich GraphQL schema from the subgraph’s entity schema.

Generated Types

For each entity, Graph Node generates: Query types:
  • entity(id: ID!): Fetch by ID
  • entities(...): Fetch multiple with filtering
Filter input types:
  • Entity_filter: All available filters for entity
Order types:
  • Entity_orderBy: Enum of sortable fields
Example:
Location in codebase: graphql/src/schema/ generates schema from entity definitions

Advanced Query Patterns

Union Queries

Query multiple entity types (when schema supports interfaces):

Subscriptions

WebSocket-based real-time subscriptions:
Implementation: Server pushes updates when entities change Endpoint: ws://localhost:8001/subgraphs/name/<name>

Query Best Practices

Request Only Needed Fields

Don’t fetch unnecessary data. Specify exact fields required.

Use Cursor Pagination

Prefer cursor-based pagination over large offsets for better performance.

Limit Nested Queries

Deep nesting impacts performance. Flatten queries when possible.

Filter Early

Apply filters at the earliest level to reduce data processed.

Troubleshooting

Query Too Complex

Error: “Query exceeds maximum complexity” Solution:
  • Reduce query depth
  • Fetch fewer entities per query
  • Split into multiple smaller queries
  • Increase GRAPH_GRAPHQL_MAX_COMPLEXITY

Query Timeout

Error: “Query timeout” Solution:
  • Optimize filters (ensure indexed fields)
  • Reduce result size with first
  • Check PostgreSQL for missing indexes
  • Increase GRAPH_GRAPHQL_QUERY_TIMEOUT

Inconsistent Results

Cause: Querying during active indexing Solution:
  • Check _meta.hasIndexingErrors
  • Wait for subgraph to sync (_meta.block.number)
  • Use block: {number: X} for consistent snapshots

Next Steps

Indexing

Understand how data is indexed before querying

Architecture

Learn about Graph Node’s overall architecture

Subgraphs

Deep dive into subgraph structure and manifests

SQL Query Generation

Technical details on SQL generation