Skip to content

Commit 9030e4c

Browse files
authored
Updating Best Practices (#959)
* Updating * adding feedback
1 parent 196624c commit 9030e4c

File tree

1 file changed

+99
-89
lines changed

1 file changed

+99
-89
lines changed

website/src/pages/en/subgraphs/querying/best-practices.mdx

Lines changed: 99 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
title: Querying Best Practices
33
---
44

5-
The Graph provides a decentralized way to query data from blockchains. Its data is exposed through a GraphQL API, making it easier to query with the GraphQL language.
6-
7-
Learn the essential GraphQL language rules and best practices to optimize your Subgraph.
5+
Use The Graph's GraphQL API to query [Subgraph](/subgraphs/developing/subgraphs/) data efficiently. This guide outlines essential GraphQL rules, guides, and best practices to help you write optimized, reliable queries.
86

97
---
108

119
## Querying a GraphQL API
1210

1311
### The Anatomy of a GraphQL Query
1412

15-
Unlike REST API, a GraphQL API is built upon a Schema that defines which queries can be performed.
13+
> GraphQL queries use the GraphQL language, which is defined in the [GraphQL specification](https://spec.graphql.org/).
14+
15+
Unlike REST APIs, GraphQL APIs are built on a schema-driven design that defines which queries can be performed.
1616

17-
For example, a query to get a token using the `token` query will look as follows:
17+
Here's a typical query to fetch a `token`:
1818

1919
```graphql
2020
query GetToken($id: ID!) {
@@ -25,7 +25,7 @@ query GetToken($id: ID!) {
2525
}
2626
```
2727

28-
which will return the following predictable JSON response (_when passing the proper `$id` variable value_):
28+
which will return a predictable JSON response (when passing the proper `$id` variable value):
2929

3030
```json
3131
{
@@ -36,8 +36,6 @@ which will return the following predictable JSON response (_when passing the pro
3636
}
3737
```
3838

39-
GraphQL queries use the GraphQL language, which is defined upon [a specification](https://spec.graphql.org/).
40-
4139
The above `GetToken` query is composed of multiple language parts (replaced below with `[...]` placeholders):
4240

4341
```graphql
@@ -50,33 +48,31 @@ query [operationName]([variableName]: [variableType]) {
5048
}
5149
```
5250

53-
## Rules for Writing GraphQL Queries
51+
### Rules for Writing GraphQL Queries
5452

55-
- Each `queryName` must only be used once per operation.
56-
- Each `field` must be used only once in a selection (we cannot query `id` twice under `token`)
57-
- Some `field`s or queries (like `tokens`) return complex types that require a selection of sub-field. Not providing a selection when expected (or providing one when not expected - for example, on `id`) will raise an error. To know a field type, please refer to [Graph Explorer](/subgraphs/explorer/).
58-
- Any variable assigned to an argument must match its type.
59-
- In a given list of variables, each of them must be unique.
60-
- All defined variables must be used.
53+
> Important: Failing to follow these rules will result in an error from The Graph API.
6154
62-
> Note: Failing to follow these rules will result in an error from The Graph API.
55+
1. Each `queryName` must only be used once per operation.
56+
2. Each `field` must be used only once in a selection (you cannot query `id` twice under `token`).
57+
3. Complex types require a selection of sub-fields.
58+
- For example, some `fields' or queries (like `tokens`) return complex types which will require a selection of sub-fields. Not providing a selection when expected or providing one when not expected will raise an error, such as `id`. To know a field type, please refer to [Graph Explorer](/subgraphs/explorer/).
59+
4. Any variable assigned to an argument must match its type.
60+
5. Variables must be uniquely defined and used.
6361

64-
For a complete list of rules with code examples, check out [GraphQL Validations guide](/resources/migration-guides/graphql-validations-migration-guide/).
62+
**For a complete list of rules with code examples, check out the [GraphQL Validations guide](/resources/migration-guides/graphql-validations-migration-guide/)**.
6563

66-
### Sending a query to a GraphQL API
64+
### How to Send a Query to a GraphQL API
6765

68-
GraphQL is a language and set of conventions that transport over HTTP.
66+
[GraphQL is a query language](https://graphql.org/learn/) and a set of conventions for APIs, typically used over HTTP to request and send data between clients and servers. This means that you can query a GraphQL API using standard `fetch` (natively or via `@whatwg-node/fetch` or `isomorphic-fetch`).
6967

70-
It means that you can query a GraphQL API using standard `fetch` (natively or via `@whatwg-node/fetch` or `isomorphic-fetch`).
71-
72-
However, as mentioned in ["Querying from an Application"](/subgraphs/querying/from-an-application/), it's recommended to use `graph-client`, which supports the following unique features:
68+
However, as recommended in [Querying from an Application](/subgraphs/querying/from-an-application/), it's best to use `graph-client`, which supports the following unique features:
7369

7470
- Cross-chain Subgraph Handling: Querying from multiple Subgraphs in a single query
7571
- [Automatic Block Tracking](https://github.com/graphprotocol/graph-client/blob/main/packages/block-tracking/README.md)
7672
- [Automatic Pagination](https://github.com/graphprotocol/graph-client/blob/main/packages/auto-pagination/README.md)
7773
- Fully typed result
7874

79-
Here's how to query The Graph with `graph-client`:
75+
Example query using `graph-client`:
8076

8177
```tsx
8278
import { execute } from '../.graphclient'
@@ -100,15 +96,15 @@ async function main() {
10096
main()
10197
```
10298

103-
More GraphQL client alternatives are covered in ["Querying from an Application"](/subgraphs/querying/from-an-application/).
99+
For more alternatives, see ["Querying from an Application"](/subgraphs/querying/from-an-application/).
104100

105101
---
106102

107103
## Best Practices
108104

109-
### Always write static queries
105+
### 1. Always Write Static Queries
110106

111-
A common (bad) practice is to dynamically build query strings as follows:
107+
A common bad practice is to dynamically build a query string as follows:
112108

113109
```tsx
114110
const id = params.id
@@ -124,14 +120,16 @@ query GetToken {
124120
// Execute query...
125121
```
126122

127-
While the above snippet produces a valid GraphQL query, **it has many drawbacks**:
123+
While the example above produces a valid GraphQL query, it comes with several issues:
124+
125+
- The full query is harder to understand.
126+
- Developers are responsible for safely sanitizing the string interpolation.
127+
- Not sending the values of the variables as part of the request can block server-side caching.
128+
- It prevents tools from statically analyzing the query (e.g.linters or type generation tools).
128129

129-
- it makes it **harder to understand** the query as a whole
130-
- developers are **responsible for safely sanitizing the string interpolation**
131-
- not sending the values of the variables as part of the request parameters **prevent possible caching on server-side**
132-
- it **prevents tools from statically analyzing the query** (ex: Linter, or type generations tools)
130+
Instead, it's recommended to **always write queries as static strings**.
133131

134-
For this reason, it is recommended to always write queries as static strings:
132+
Example of static string:
135133

136134
```tsx
137135
import { execute } from 'your-favorite-graphql-client'
@@ -153,18 +151,21 @@ const result = await execute(query, {
153151
})
154152
```
155153

156-
Doing so brings **many advantages**:
154+
Static strings have several **key advantages**:
157155

158-
- **Easy to read and maintain** queries
159-
- The GraphQL **server handles variables sanitization**
160-
- **Variables can be cached** at server-level
161-
- **Queries can be statically analyzed by tools** (more on this in the following sections)
156+
- Queries are easier to read, manage, and debug.
157+
- Variable sanitization is handled by the GraphQL server The GraphQL.
158+
- Variables can be cached at the server level.
159+
- Queries can be statically analyzed by tools (see [GraphQL Essential Tools](/subgraphs/querying/best-practices/#graphql-essential-tools-guides)).
162160

163-
### How to include fields conditionally in static queries
161+
### 2. Include Fields Conditionally in Static Queries
164162

165-
You might want to include the `owner` field only on a particular condition.
163+
Including fields in static queries only for a particular condition improves performance and keeps responses lightweight by fetching only the necessary data when it's relevant.
166164

167-
For this, you can leverage the `@include(if:...)` directive as follows:
165+
- The `@include(if:...)` directive tells the query to **include** a specific field only if the given condition is true.
166+
- The `@skip(if: ...)` directive tells the query to **exclude** a specific field if the given condition is true.
167+
168+
Example using `owner` field with `@include(if:...)` directive:
168169

169170
```tsx
170171
import { execute } from 'your-favorite-graphql-client'
@@ -187,15 +188,11 @@ const result = await execute(query, {
187188
})
188189
```
189190

190-
> Note: The opposite directive is `@skip(if: ...)`.
191-
192-
### Ask for what you want
193-
194-
GraphQL became famous for its "Ask for what you want" tagline.
191+
### 3. Ask Only For What You Want
195192

196-
For this reason, there is no way, in GraphQL, to get all available fields without having to list them individually.
193+
GraphQL is known for its "Ask for what you want” tagline, which is why it requires explicitly listing each field you want. There's no built-in way to fetch all available fields automatically.
197194

198-
- When querying GraphQL APIs, always think of querying only the fields that will be actually used.
195+
- When querying GraphQL APIs, always think of querying only the fields that will actually be used.
199196
- Make sure queries only fetch as many entities as you actually need. By default, queries will fetch 100 entities in a collection, which is usually much more than what will actually be used, e.g., for display to the user. This applies not just to top-level collections in a query, but even more so to nested collections of entities.
200197

201198
For example, in the following query:
@@ -215,9 +212,9 @@ query listTokens {
215212

216213
The response could contain 100 transactions for each of the 100 tokens.
217214

218-
If the application only needs 10 transactions, the query should explicitly set `first: 10` on the transactions field.
215+
If the application only needs 10 transactions, the query should explicitly set **`first: 10`** on the transactions field.
219216

220-
### Use a single query to request multiple records
217+
### 4. Use a Single Query to Request Multiple Records
221218

222219
By default, Subgraphs have a singular entity for one record. For multiple records, use the plural entities and filter: `where: {id_in:[X,Y,Z]}` or `where: {volume_gt:100000}`
223220

@@ -249,7 +246,7 @@ query ManyRecords {
249246
}
250247
```
251248

252-
### Combine multiple queries in a single request
249+
### 5. Combine Multiple Queries in a Single Request
253250

254251
Your application might require querying multiple types of data as follows:
255252

@@ -281,9 +278,9 @@ const [tokens, counters] = Promise.all(
281278
)
282279
```
283280

284-
While this implementation is totally valid, it will require two round trips with the GraphQL API.
281+
While this implementation is valid, it will require two round trips with the GraphQL API.
285282

286-
Fortunately, it is also valid to send multiple queries in the same GraphQL request as follows:
283+
It's best to send multiple queries in the same GraphQL request as follows:
287284

288285
```graphql
289286
import { execute } from "your-favorite-graphql-client"
@@ -304,9 +301,9 @@ query GetTokensandCounters {
304301
const { result: { tokens, counters } } = execute(query)
305302
```
306303

307-
This approach will **improve the overall performance** by reducing the time spent on the network (saves you a round trip to the API) and will provide a **more concise implementation**.
304+
Sending multiple queries in the same GraphQL request **improves the overall performance** by reducing the time spent on the network (saves you a round trip to the API) and provides a **more concise implementation**.
308305

309-
### Leverage GraphQL Fragments
306+
### 6. Leverage GraphQL Fragments
310307

311308
A helpful feature to write GraphQL queries is GraphQL Fragment.
312309

@@ -335,7 +332,7 @@ Such repeated fields (`id`, `active`, `status`) bring many issues:
335332
- More extensive queries become harder to read.
336333
- When using tools that generate TypeScript types based on queries (_more on that in the last section_), `newDelegate` and `oldDelegate` will result in two distinct inline interfaces.
337334

338-
A refactored version of the query would be the following:
335+
An optimized version of the query would be the following:
339336

340337
```graphql
341338
query {
@@ -359,27 +356,27 @@ fragment DelegateItem on Transcoder {
359356
}
360357
```
361358

362-
Using GraphQL `fragment` will improve readability (especially at scale) and result in better TypeScript types generation.
359+
Using a GraphQL `fragment` improves readability (especially at scale) and results in better TypeScript types generation.
363360

364361
When using the types generation tool, the above query will generate a proper `DelegateItemFragment` type (_see last "Tools" section_).
365362

366-
### GraphQL Fragment do's and don'ts
363+
## GraphQL Fragment Guidelines
367364

368-
### Fragment base must be a type
365+
### Do's and Don'ts for Fragments
369366

370-
A Fragment cannot be based on a non-applicable type, in short, **on type not having fields**:
367+
1. Fragments cannot be based on a non-applicable types (types without fields).
368+
2. `BigInt` cannot be used as a fragment's base because it's a **scalar** (native "plain" type).
369+
370+
Example:
371371

372372
```graphql
373373
fragment MyFragment on BigInt {
374374
# ...
375375
}
376376
```
377377

378-
`BigInt` is a **scalar** (native "plain" type) that cannot be used as a fragment's base.
379-
380-
#### How to spread a Fragment
381-
382-
Fragments are defined on specific types and should be used accordingly in queries.
378+
3. Fragments belong to specific types and must be used with those same types in queries.
379+
4. Spread only fragments matching the correct type.
383380

384381
Example:
385382

@@ -402,20 +399,23 @@ fragment VoteItem on Vote {
402399
}
403400
```
404401

405-
`newDelegate` and `oldDelegate` are of type `Transcoder`.
402+
- `newDelegate` and `oldDelegate` are of type `Transcoder`. It's not possible to spread a fragment of type `Vote` here.
406403

407-
It is not possible to spread a fragment of type `Vote` here.
404+
5. Fragments must be defined based on their specific usage.
405+
6. Define fragments as an atomic business unit of data.
408406

409-
#### Define Fragment as an atomic business unit of data
407+
---
410408

411-
GraphQL `Fragment`s must be defined based on their usage.
409+
### How to Define `Fragment` as an Atomic Business Unit of Data
412410

413-
For most use-case, defining one fragment per type (in the case of repeated fields usage or type generation) is sufficient.
411+
> For most use-cases, defining one fragment per type (in the case of repeated fields usage or type generation) is enough.
414412
415413
Here is a rule of thumb for using fragments:
416414

417415
- When fields of the same type are repeated in a query, group them in a `Fragment`.
418-
- When similar but different fields are repeated, create multiple fragments, for instance:
416+
- When similar but different fields are repeated, create multiple fragments.
417+
418+
Example:
419419

420420
```graphql
421421
# base fragment (mostly used in listing)
@@ -438,47 +438,57 @@ fragment VoteWithPoll on Vote {
438438

439439
---
440440

441-
## The Essential Tools
441+
## GraphQL Essential Tools Guides
442+
443+
### Test Queries with Graph Explorer
444+
445+
Before integrating GraphQL queries into your dapp, it's best to test them. Instead of running them directly in your app, use a web-based playground.
446+
447+
Start with [Graph Explorer](https://thegraph.com/explorer), a preconfigured GraphQL playground built specifically for Subgraphs. You can experiment with queries and see the structure of the data returned without writing any frontend code.
448+
449+
If you want alternatives to debug/test your queries, check out other similar web-based tools:
450+
451+
- [GraphiQL](https://graphiql-online.com/graphiql)
452+
- [Altair](https://altairgraphql.dev/)
442453

443-
### GraphQL web-based explorers
454+
### Setting up Workflow and IDE Tools
444455

445-
Iterating over queries by running them in your application can be cumbersome. For this reason, don't hesitate to use [Graph Explorer](https://thegraph.com/explorer) to test your queries before adding them to your application. Graph Explorer will provide you a preconfigured GraphQL playground to test your queries.
456+
In order to keep up with querying best practices and syntax rules, use the following workflow and IDE tools.
446457

447-
If you are looking for a more flexible way to debug/test your queries, other similar web-based tools are available such as [Altair](https://altairgraphql.dev/) and [GraphiQL](https://graphiql-online.com/graphiql).
458+
#### GraphQL ESLint
448459

449-
### GraphQL Linting
460+
1. Install GraphQL ESLint
450461

451-
In order to keep up with the mentioned above best practices and syntactic rules, it is highly recommended to use the following workflow and IDE tools.
462+
Use [GraphQL ESLint](https://the-guild.dev/graphql/eslint/docs/getting-started) to enforce best practices and syntax rules with zero effort.
452463

453-
**GraphQL ESLint**
464+
2. Use the ["operations-recommended"](https://the-guild.dev/graphql/eslint/docs/configs) config
454465

455-
[GraphQL ESLint](https://the-guild.dev/graphql/eslint/docs/getting-started) will help you stay on top of GraphQL best practices with zero effort.
466+
This will enforce essential rules such as:
456467

457-
[Setup the "operations-recommended"](https://the-guild.dev/graphql/eslint/docs/configs) config will enforce essential rules such as:
468+
- `@graphql-eslint/fields-on-correct-type`: Ensures fields match the proper type.
469+
- `@graphql-eslint/no-unused variables`: Flags unused variables in your queries.
458470

459-
- `@graphql-eslint/fields-on-correct-type`: is a field used on a proper type?
460-
- `@graphql-eslint/no-unused variables`: should a given variable stay unused?
461-
- and more!
471+
Result: You'll **catch errors without even testing queries** on the playground or running them in production!
462472

463-
This will allow you to **catch errors without even testing queries** on the playground or running them in production!
473+
#### Use IDE plugins
464474

465-
### IDE plugins
475+
GraphQL plugins streamline your workflow by offering real-time feedback while you code. They highlight mistakes, suggest completions, and help you explore your schema faster.
466476

467-
**VSCode and GraphQL**
477+
1. VS Code
468478

469-
The [GraphQL VSCode extension](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql) is an excellent addition to your development workflow to get:
479+
Install the [GraphQL VS Code extension](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql) to unlock:
470480

471481
- Syntax highlighting
472482
- Autocomplete suggestions
473483
- Validation against schema
474484
- Snippets
475485
- Go to definition for fragments and input types
476486

477-
If you are using `graphql-eslint`, the [ESLint VSCode extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) is a must-have to visualize errors and warnings inlined in your code correctly.
487+
If you are using `graphql-eslint`, use the [ESLint VS Code extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) to visualize errors and warnings inlined in your code correctly.
478488

479-
**WebStorm/Intellij and GraphQL**
489+
2. WebStorm/Intellij and GraphQL
480490

481-
The [JS GraphQL plugin](https://plugins.jetbrains.com/plugin/8097-graphql/) will significantly improve your experience while working with GraphQL by providing:
491+
Install the [JS GraphQL plugin](https://plugins.jetbrains.com/plugin/8097-graphql/). It significantly improves the experience of working with GraphQL by providing:
482492

483493
- Syntax highlighting
484494
- Autocomplete suggestions

0 commit comments

Comments
 (0)