Skip to content

Commit 370b364

Browse files
authored
feat: added query conversion page (#771)
1 parent ff7d0a7 commit 370b364

3 files changed

Lines changed: 345 additions & 1 deletion

File tree

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
---
2+
id: query-conversion
3+
title: Query Conversion Guide
4+
sidebar_label: Query Conversion
5+
slug: /query-conversion
6+
description: Learn how to convert queries from TheGraph's custom GraphQL syntax to Envio's standard GraphQL syntax.
7+
---
8+
9+
# Query Conversion
10+
11+
Envio uses standard GraphQL query language, while TheGraph uses a custom GraphQL syntax. While the queries are very similar, there are some important differences to be aware of when migrating.
12+
13+
This guide covers all the differences between TheGraph's query syntax and Envio's query syntax, with examples for each conversion rule.
14+
15+
## Converter Tool
16+
17+
We've built a [query converter tool](https://github.com/enviodev/subgraph-to-hyperindex-query-converter) that automatically converts TheGraph queries to Envio's GraphQL syntax. You can:
18+
19+
- **Convert and execute**: Provide your Envio GraphQL endpoint and a query written in TheGraph syntax. The tool will convert it, execute it against your endpoint, and return the results
20+
- **Convert only**: Use the tool to convert queries and view the converted output without executing them. To convert queries without executing them, add `/debug` to the converter endpoint and send your query. The response will contain only the converted query without actually running it.
21+
22+
**Repository**: [subgraph-to-hyperindex-query-converter](https://github.com/enviodev/subgraph-to-hyperindex-query-converter)
23+
24+
:::info Availability
25+
The query converter tool is only available to users on paid tiers.
26+
:::
27+
28+
:::info Setup Assistance
29+
If you'd like to use the query converter tool for your indexer, please reach out to our team and we can add an instance of the converter tool to be deployed with your indexer.
30+
:::
31+
32+
:::tip Best Practice
33+
**We strongly recommend converting your queries to use Envio's standard GraphQL syntax** rather than relying on the query converter tool. This ensures better performance, maintainability, and avoids potential conversion limitations. The converter tool is primarily intended for backwards compatibility in cases where you might have external-facing APIs which depend on TheGraph's query syntax.
34+
:::
35+
36+
:::warning Beta Status
37+
This converter tool is still very much in beta. We're actively working on it and discovering new query conversions that need to be handled.
38+
**If you encounter any conversion failures or incorrect conversions, please [file a GitHub issue](https://github.com/enviodev/subgraph-to-hyperindex-query-converter/issues) in the repository so we can address it.**
39+
:::
40+
41+
### Converter Tool Limitations
42+
43+
The query converter tool has several limitations to be aware of:
44+
45+
- **Rate limits**: The rate limits of your associated tier will still apply when using the converter tool.
46+
- **Beta status and incomplete coverage**: Since the tool is in beta there is a possibility we have missed some conversion patterns. Some queries may still fail to convert correctly or may not be handled at all. If you encounter any conversion failures or incorrect conversions, please [file a GitHub issue](https://github.com/enviodev/subgraph-to-hyperindex-query-converter/issues) in the repository so we can address it.
47+
- **WebSocket subscriptions**: WebSocket subscriptions are not supported. The converter only works with standard HTTP GraphQL queries.
48+
- **Directives not supported**: GraphQL directives (such as `@skip`, `@include`, etc.) are not supported because the converter uses simple string parsing rather than a full GraphQL parser. Directives are ignored or may break parsing.
49+
- **Variables in orderBy/orderDirection**: Variables used in `orderBy` and `orderDirection` parameters are ignored because HyperIndex requires literal field names in `order_by` clauses, and variable values are unknown at conversion time. Queries using variables for ordering will return unordered results.
50+
- **Array filter operators**: The `_containsAny` and `_containsAll` filters are TheGraph-specific array operators that don't have direct Hasura equivalents. The converter explicitly rejects these and returns an `UnsupportedFilter` error. Use `_in` for array matching instead.
51+
- **Meta queries**: Meta queries only support `_meta { block { number } }` because HyperIndex exposes block information differently. Other `_meta` fields (hash, timestamp, deployment, hasIndexingErrors) are not available in HyperIndex's schema and will return a `ComplexMetaQuery` error.
52+
- **Introspection queries**: Introspection queries only work if they use the operation name `"IntrospectionQuery"`. Other introspection queries (like querying `__schema` directly) will fail because they go through normal conversion which doesn't understand introspection syntax.
53+
54+
For production applications, we recommend migrating your queries to Envio's standard GraphQL syntax to avoid these limitations.
55+
56+
---
57+
58+
### 1. Entity Name Conversion
59+
60+
**Rule**: TheGraph uses pluralized entity names (e.g., `pools`, `factories`, `tokens`), while Envio uses the entity name as-is from the schema (singular, PascalCase). When converting, plural entity names are automatically singularized and capitalized to match Envio's schema.
61+
62+
**Example**:
63+
64+
```graphql
65+
# TheGraph
66+
query {
67+
pools { id }
68+
factories { id }
69+
tokens { id }
70+
}
71+
72+
# Envio
73+
query {
74+
Pool { id }
75+
Factory { id }
76+
Token { id }
77+
}
78+
```
79+
80+
Single entity queries use `EntityName_by_pk` (by primary key) in Envio. Alternatively, you could use a `where` clause with the primary key field:
81+
82+
```graphql
83+
# TheGraph
84+
query {
85+
pool(id: "0x123") { value }
86+
}
87+
88+
# Envio
89+
query {
90+
Pool_by_pk(id: "0x123") { value }
91+
}
92+
```
93+
94+
Or using a `where` clause:
95+
96+
```graphql
97+
# Envio
98+
query {
99+
Pool(where: {id: {_eq: "0x123"}}) { value }
100+
}
101+
```
102+
103+
### 2. Pagination Parameters
104+
105+
#### First → Limit
106+
107+
**Rule**: The `first` parameter is converted to `limit`.
108+
109+
**Example**:
110+
111+
```graphql
112+
# TheGraph
113+
query {
114+
pools(first: 10) { id }
115+
}
116+
117+
# Envio
118+
query {
119+
Pool(limit: 10) { id }
120+
}
121+
```
122+
123+
#### Skip → Offset
124+
125+
**Rule**: The `skip` parameter is converted to `offset`.
126+
127+
**Example**:
128+
129+
```graphql
130+
# TheGraph
131+
query {
132+
pools(skip: 20) { id }
133+
}
134+
135+
# Envio
136+
query {
137+
Pool(offset: 20) { id }
138+
}
139+
```
140+
141+
### 3. Ordering Parameters
142+
143+
#### OrderBy and OrderDirection → Order_by
144+
145+
**Rule**: The `orderBy` and `orderDirection` parameters are combined into a single `order_by: {field: direction}` clause.
146+
147+
**Example**:
148+
149+
```graphql
150+
# TheGraph
151+
query {
152+
pools(orderBy: name, orderDirection: desc) { id name }
153+
# Use orderDirection: asc for ascending order
154+
}
155+
156+
# Envio
157+
query {
158+
Pool(order_by: {name: desc}) { id name }
159+
# Use asc for ascending order, e.g., order_by: {name: asc}
160+
}
161+
```
162+
163+
### 4. Filter Operators
164+
165+
#### Equality Filter
166+
167+
**Rule**: Simple equality filters are converted to `where: {field: {_eq: value}}` format.
168+
169+
**Example**:
170+
171+
```graphql
172+
# TheGraph
173+
query {
174+
pools(name: "test") { id name }
175+
}
176+
177+
# Envio
178+
query {
179+
Pool(where: {name: {_eq: "test"}}) { id name }
180+
}
181+
```
182+
183+
#### Comparison Filters
184+
185+
**Rule**: Comparison filters (`_not`, `_gt`, `_gte`, `_lt`, `_lte`, `_in`, `_not_in`) are converted to their Hasura equivalents.
186+
187+
**Examples**:
188+
189+
```graphql
190+
# TheGraph
191+
query {
192+
pools(id_not: "0x123") { id }
193+
pools(amount_gt: 100) { id amount }
194+
pools(amount_gte: 100) { id amount }
195+
pools(timestamp_lt: 1650000000) { id timestamp }
196+
pools(timestamp_lte: 1650000000) { id timestamp }
197+
pools(id_in: ["1", "2", "3"]) { id name }
198+
pools(id_not_in: ["1", "2", "3"]) { id name }
199+
}
200+
201+
# Envio
202+
query {
203+
Pool(where: {id: {_neq: "0x123"}}) { id }
204+
Pool(where: {amount: {_gt: 100}}) { id amount }
205+
Pool(where: {amount: {_gte: 100}}) { id amount }
206+
Pool(where: {timestamp: {_lt: 1650000000}}) { id timestamp }
207+
Pool(where: {timestamp: {_lte: 1650000000}}) { id timestamp }
208+
Pool(where: {id: {_in: ["1", "2", "3"]}}) { id name }
209+
Pool(where: {id: {_nin: ["1", "2", "3"]}}) { id name }
210+
}
211+
```
212+
213+
#### String Filters
214+
215+
**Rule**: String filters (`_contains`, `_starts_with`, `_ends_with`, and their `_not` and `_nocase` variants) are converted to `_ilike` with appropriate wildcards. The `%` symbol represents any text at that position in the pattern.
216+
217+
**Examples**:
218+
219+
```graphql
220+
# TheGraph
221+
query {
222+
pools(name_contains: "test") { id name }
223+
pools(name_not_contains: "test") { id name }
224+
pools(symbol_starts_with: "ABC") { id symbol }
225+
pools(symbol_ends_with: "XYZ") { id symbol }
226+
pools(name_not_starts_with: "A") { id name }
227+
pools(name_not_ends_with: "x") { id name }
228+
pools(name_contains_nocase: "test") { id name }
229+
pools(name_starts_with_nocase: "test") { id name }
230+
pools(name_ends_with_nocase: "test") { id name }
231+
}
232+
233+
# Envio
234+
query {
235+
Pool(where: {name: {_ilike: "%test%"}}) { id name }
236+
Pool(where: {_not: {name: {_ilike: "%test%"}}}) { id name }
237+
Pool(where: {symbol: {_ilike: "ABC%"}}) { id symbol }
238+
Pool(where: {symbol: {_ilike: "%XYZ"}}) { id symbol }
239+
Pool(where: {_not: {name: {_ilike: "A%"}}}) { id name }
240+
Pool(where: {_not: {name: {_ilike: "%x"}}}) { id name }
241+
Pool(where: {name: {_ilike: "%test%"}}) { id name }
242+
Pool(where: {name: {_ilike: "test%"}}) { id name }
243+
Pool(where: {name: {_ilike: "%test"}}) { id name }
244+
}
245+
```
246+
247+
### 5. Variable Type Conversions
248+
249+
#### ID → String
250+
251+
**Rule**: Variable types `ID` and `ID!` are converted to `String` and `String!` respectively.
252+
253+
**Example**:
254+
255+
```graphql
256+
# TheGraph
257+
query getPoolValue($id: ID!) {
258+
pool(id: $id) { value }
259+
}
260+
261+
# Envio
262+
query getPoolValue($id: String!) {
263+
Pool_by_pk(id: $id) { value }
264+
}
265+
```
266+
267+
#### Bytes → String
268+
269+
**Rule**: Variable types `Bytes` and `Bytes!` are converted to `String` and `String!` respectively.
270+
271+
**Example**:
272+
273+
```graphql
274+
# TheGraph
275+
query getTokens($id: Bytes) {
276+
tokens(where: { id: $id }) { id timestamp }
277+
}
278+
279+
# Envio
280+
query getTokens($id: String) {
281+
Token(where: {id: {_eq: $id}}) { id timestamp }
282+
}
283+
```
284+
285+
#### BigInt → numeric
286+
287+
**Rule**: Variable types `BigInt` and `BigInt!` are converted to `numeric` and `numeric!` respectively.
288+
289+
**Example**:
290+
291+
```graphql
292+
# TheGraph
293+
query GetTokens($amount: BigInt) {
294+
tokens(where: { amount: $amount }) { id amount }
295+
}
296+
297+
# Envio
298+
query GetTokens($amount: numeric) {
299+
Token(where: {amount: {_eq: $amount}}) { id amount }
300+
}
301+
```
302+
303+
#### BigDecimal → numeric
304+
305+
**Rule**: Variable types `BigDecimal` and `BigDecimal!` are converted to `numeric` and `numeric!` respectively.
306+
307+
**Example**:
308+
309+
```graphql
310+
# TheGraph
311+
query GetValue($value: BigDecimal!) {
312+
pools(where: { value: $value }) { id }
313+
}
314+
315+
# Envio
316+
query GetValue($value: numeric!) {
317+
Pool(where: {value: {_eq: $value}}) { id }
318+
}
319+
```
320+
321+
---
322+
323+
## Summary Table
324+
325+
| Category | TheGraph | Envio | Example |
326+
|----------|-----------|-------|---------|
327+
| **Entity Names** | Plural camelCase | Singular PascalCase (as-is from schema) | `pools``Pool` |
328+
| **Pagination** | `first`, `skip` | `limit`, `offset` | `first: 10, skip: 20``limit: 10, offset: 20` |
329+
| **Ordering** | `orderBy`, `orderDirection` | `order_by: {field: direction}` | `orderBy: name, orderDirection: desc``order_by: {name: desc}` |
330+
| **Equality Filter** | `field: value` | `field: {_eq: value}` | `name: "test"``name: {_eq: "test"}` |
331+
| **Comparison Filters** | `field_gt`, `field_gte`, etc. | `field: {_gt: value}`, etc. | `amount_gt: 100``amount: {_gt: 100}` |
332+
| **String Filters** | `_contains`, `_starts_with`, etc. | `_ilike` with `%` wildcards | `name_contains: "test"``name: {_ilike: "%test%"}` |
333+
| **Variable Types** | `ID`, `Bytes`, `BigInt`, `BigDecimal` | `String`, `numeric` | `$id: ID!``$id: String!` |
334+
335+
---
336+
337+
## Getting Help
338+
339+
If you encounter any issues with query conversion or have questions:
340+
341+
- **Converter Issues**: File a [GitHub issue](https://github.com/enviodev/subgraph-to-hyperindex-query-converter/issues) for the converter tool
342+
- **General Questions**: Join our [Discord community](https://discord.gg/envio) for support
343+

docs/HyperIndex/migration-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ HyperIndex is a powerful tool that can be used to index any contract. There are
196196
- Use the `field_selection` option to add additional fields to your index. Doc here: [field selection](../HyperIndex/configuration-file#field-selection)
197197
- Use the `unordered_multichain_mode` option to enable unordered multichain mode, this is the most common need for multichain indexing. However comes with tradeoffs worth understanding. Doc here: [unordered multichain mode](../HyperIndex/configuration-file#unordered-multichain-mode)
198198
- Use wildcard indexing to index by event signatures rather than by contract address.
199-
- HyperIndex uses the standard graphql query language, where as the subgraph uses a custom query language. You can read about the slight nuances [here](https://docs.sablier.com/api/caveats). (We are working on a basic tool to help with backwards compatibility, please check in with us on discord for it's current status).
199+
- HyperIndex uses the standard GraphQL query language, whereas TheGraph uses a custom GraphQL syntax. You can read about the differences and how to convert queries in our [Query Conversion Guide](/docs/HyperIndex/query-conversion). We also provide a query converter tool for backwards compatibility with existing TheGraph queries.
200200
- Loaders are a powerful feature to optimize historical sync performance. You can read more about them [here](../HyperIndex/loaders).
201201
- HyperIndex is very flexible and can be used to index offchain data too or send messages to a queue etc for fetching external data, you can further optimise the fetching by using the [effects api](/docs/HyperIndex/effect-api)
202202

sidebarsHyperIndex.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ module.exports = {
127127
"Advanced/performance/benchmarking",
128128
"Advanced/loaders",
129129
"Advanced/websockets",
130+
"Advanced/query-conversion",
130131
// {
131132
// type: "category",
132133
// label: "Performance",

0 commit comments

Comments
 (0)