GraphQL Query Efficiency
Using GraphQL offers many benefits but it can be easy to write complex queries that are slow to execute. Here's some best practices to make your GraphQL queries more efficient.
1. Avoid deep nesting
GraphQL allows you to use a single request for nested entities in a single query. Deeply nested queries can result in extensive database joins, or complex data fetching logic — increasing the execution time.
In the Core Administrate GraphQL API we implement a DataLoader to improve the batch loading for nested entities, but there are limitations to what can be optimized.
Nesting of connections (where nested edges
/node
syntax is used) is one of the more expensive operations where multiple database queries will be required.
An example of a complex query might request Learners and their Events. This creates a deeply nested structure fetching Events data for each Learner object:
query nestedLearners {
learners {
edges {
node {
id
contact {
personalName {
firstName
lastName
}
}
event {
id
title
classroomStart
classroomEnd
sessions {
edges {
node {
id
code
title
timeZonedStart
timeZonedEnd
}
}
}
courseTemplate {
id
lmsSummary
}
}
}
}
}
}
To reduce the complexity, nested queries could be broken down into smaller, more manageable parts. The first query can fetch a list of Learners
query learners {
learners {
edges {
node {
id
contact {
personalName {
firstName
lastName
}
}
event {
id
}
}
}
}
}
The second one fetches Events separately
query events{
events(filters: {field: id, operation:in, values: ["eventid1", "eventid2"]}) {
edges {
node {
id
title
classroomStart
classroomEnd
sessions {
edges {
node {
id
code
title
timeZonedStart
timeZonedEnd
}
}
}
courseTemplate {
id
lmsSummary
}
}
}
}
}
2. Avoid over-fetching of fields
One of the main advantages of GraphQL is to avoid over-fetching of information. Over-fetching can lead to increased execution time, as unnecessary data is queried and transferred.
Consider a query that requests all available information about an Event, even when only a few fields are required:
query events {
events {
edges {
node {
id
title
classroomStart
classroomEnd
bookedPlaces
code
defaultPrice {
id
}
end
isSoldOut
learningMode
displayId
minPlaces
maxPlaces
type
sessions {
edges {
node {
id
code
title
timeZonedStart
timeZonedEnd
}
}
}
courseTemplate {
id
lmsSummary
}
}
}
}
}
This could be rewritten as the smaller query
query eventsShort {
events {
edges {
node {
id
title
sessions {
edges {
node {
id
code
title
timeZonedStart
timeZonedEnd
}
}
}
}
}
}
}
3. Use aliasing to avoid fetching all custom fields
When Custom fields required to be fetched, there are three ways to do it
Query for all Custom fields:
query allCustomFields {
events {
edges {
node {
id
code
customFieldValues {
definition{
key
label
}
value
}
}
}
}
}
Query for single Custom Field:
query singleCustomField {
events {
edges {
node {
id
code
customFieldValues(filter:{definitionKey: "Q3VzdG9tRmllbGREZWZpbml0aW9uOjI2", }) {
definition{
key
label
}
value
}
}
}
}
}
Query certain Custom Fields using Aliases
query severalCustomFields {
events {
edges {
node {
id
code
customAlias1: customFieldValues(filter:{definitionKey: "Q3VzdG9tRmllbGREZWZpbml0aW9uOjMz"}) {
definition{
key
label
}
value
}
customAlias2: customFieldValues(filter:{definitionKey: "Q3VzdG9tRmllbGREZWZpbml0aW9uOjM1"}) {
definition{
key
label
}
value
}
}
}
}
}
4. Paginating Results
When fetching a large number of fields, it can be useful to paginate the results to limit the amount of data returned in each response. Pagination could be implemented with first
and offset
arguments
query eventsShort {
events(first: 2, offset: 0) {
edges {
node {
id
title
}
}
}
}