ScandiPWA utilizes the Util/Query
library to dynamically generate clean and extensible GraphQL queries using JavaScript. The ScandiPWA query pattern involves defining classes under the src/query/
folder and creating methods that return an object representing the query. It's important to learn the best practices and how to create ScandiPWA queries.
There is a distinction between ScandiPWA queries and GraphQL queries: GraphQL queries are the default way of interacting with the backend through requests, while ScandiPWA queries are used to generate those queries.
<aside> 🪛 ScandiPWA communicates with the Magento server through the use of requests and has a set of patterns to make clean and extensible queries. To achieve this connection, Magento defines a GraphQL schema and it’s corresponding resolvers, enabling this communication with the frontend to happen and request the data via GraphQL.
</aside>
<aside> ➡️ To learn how to use these ScandiPWA queries and make requests check this guide!
</aside>
You might want to read the GraphQL documentation to get a full understanding of the language, but there is a quick introduction below.
GraphQL queries look like JSON objects without quotes or values, and describe which fields you want to fetch from the server:
If you make the below request to the GraphQL endpoint on a Magento instance (it's usually /graphql
), you will receive a JSON response similar to the response shown aside:
{
categoryList {
name
children {
name
url
product_count
}
}
}
{
"data": {
"categoryList": [
{
"name": "Default Category",
"children": [
{
"name": "Audio, Video & Photo Equipment",
"url": "/audio-video-photo-equipment.html",
"product_count": 0
},
{
"name": "Home Security & Automation",
"url": "/home-security-automation.html",
"product_count": 0
},
// [...]
]
}
]
}
}
In this case, you want to query the categoryList
field and fetch its name and children fields. For the children, you want to fetch the name, URL, and product count. It is not clear from the query, but if you were to look at the GraphQL schema, you would know that the children
field is actually an array of such objects.
<aside>
➡️ The categoryList
query has other fields that you could fetch (such as id
, description
), but GraphQL only returns the ones asked for.
</aside>
To fully leverage the power of GraphQL in ScandiPWA, it is essential that you understand how to make queries and send requests, this page aims to teach you about queries.
It's also important to ensure that the data you want to retrieve is allowed!
<aside> ℹ️ Technically, you could fetch data using a REST API as well, but this has several disadvantages and is discouraged in favor of GraphQL. The ScandiPWA codebase entirely uses GraphQL.
</aside>
To make a code clear and extensible, you want a certain layer to be responsible for queries. In the ScandiPWA, this layer is represented as a set of instructions that is responsible for defining GraphQL queries. The set consists of tools from the ScandiPWA library Util/Query
.
<aside>
➡️ The Util/Query
library helps with generating queries dynamically that can be easily extended by plugins (How to create a plugin?).
</aside>
Work with queries considers using a JavaScript class with methods related to generating queries.
By convention, ****classes are located inside the src/query/
directory. The name of a JavaScript file should have the query
postfix.
<aside>
➡️ Each class should be responsible for one entity. For instance, the Category.query.js
file contains queries for working with categories.
</aside>
Classes should be exported as instances in order to store only a single object related to certain functionality.
An example of a .query.js
file:
import { Field } from 'Util/Query';
/** @namespace Query/Review/Query */
export class ReviewQuery {
getRatingQuery() {
return new Field('productReviewRatings')
.addFieldList(this._getRatingFields());
}
// helper function
_getRatingFields() {
return [
'nestedField1',
'nestedField2'
];
}
}
// vvv Classes should be exported as instances
export default new ReviewQuery();
In your query class, you must create methods that return the query you want to access. In this example, the ReviewQuery
class defines a method getRatingQuery
to return a query with 2 nested fields.
<aside> ⚠️ Always define a namespace. This will allow your query to be extendable with the use of plugins. How to work with plugins?
</aside>
<aside>
➡️ A ScandiPWA query is a way to programmatically create GraphQL queries using the Util/Query
library, avoiding writing the entire JSON object yourself and allowing for concise configuration.
</aside>
When working with ScandiPWA queries, you can dynamically generate GraphQL queries programmatically by importing the Field
class from Util/Query
library and start working from creating an instance, as you are going to examine in the examples below.
The Field
class contains methods that will return the instance you are working with. This approach helps you maintain the query with the call chain.
<aside> ➡️ There is a whole standalone package published. It has the same functionality and is written in TypeScript. View on the Github
</aside>
To add a field you may use the .addField()
method:
import { Field } from 'Util/Query';
const exampleQuery = new Field('fieldName');
query {
fieldName
}
To make your query have nested fields you may use the .addFieldList()
method that accepts an array of strings or Field
instances:
import { Field } from 'Util/Query';
const exampleQuery = new Field('fieldName')
.addFieldList([
'nestedField'
]);
query {
fieldName {
nestedField
}
}
<aside> ⚠️ When defining a query with nested fields**, it is better** to create a function that returns the list of fields to add. This is important to allow the query to be extended, and fields to be added via plugin.
To add an additional field to the request you may use the .addField()
method:
import { Field } from 'Util/Query';
// creating a query
const exampleQuery = new Field('fieldName_1')
// adding an additional field
.addField('fieldName_2');
query {
fieldName_1
fieldName_2
}
When an additional field contains a nested one, you may create a new Field
instance, add a nested field by the .addFieldList()
method, and then add this instance by the .addField()
method:
import { Field } from 'Util/Query';
// creating a query
const exampleQuery = new Field('fieldName_1');
// creating an additional field
const exampleField = new Field('fieldName_2')
// adding a nested field
.addFieldList([
'nestedField'
]);
// adding the additional field
// with the nested one to the query
exampleQuery.addField(exampleField);
query {
fieldName_1
fieldName_2 {
nestedField
}
}
To add an argument you should use the .addArgument(name, type, value)
method:
import { Field } from 'Util/Query';
// creating a query
const exampleQuery = new Field('fieldName')
// adding an argument to a field
.addArgument(
'argName',
'argType',
'argValue'
);
query {
fieldName (
argName: argValue
) { ... }
}
name
– the name of an argumenttype
– the argument typevalue
– a value of an argumentTo add aliases you may use .setAlias()
method:
import { Field } from 'Util/Query';
// creating a query
const exampleQuery = new Field('fieldName')
// setting an alias
.setAlias('alias_1');
// creating an additional field
const exampleField = new Field('fieldName')
// setting an alias
.setAlias('alias_2');
// adding the additional field to the query
exampleQuery.addField(exampleField);
query {
alias_1: fieldName {}
alias_2: fieldName {}
}
To make sure your GraphQL query is allowed on the BE, you can make a test request. How can I test a request?
<aside> ➡️ In case a request is allowed, you may start working on methods for generating a query
In case a request is not allowed, you should create a Magento module and extend GraphQL functionality on the BE by creating a new resolver (How to create a resolver?).
</aside>
To see what the ScandiPWA query produces, you can import prepareFieldString
from Util/Query
, and use console.log
to log the produced query. For example:
import { Field, prepareFieldString } from 'Util/Query';
const exampleQuery = new Field('fieldName_1');
// creating an additional field
const exampleField = new Field('fieldName_2')
// adding a nested field
.addFieldList([
'nestedField'
]);
// adding the additional field
// with the nested one to the query
exampleQuery.addField(exampleField);
console.log(prepareFieldString(exampleQuery));
//fieldName_1{fieldName_2{nestedField}}
By using the CLI, you can easily create a new query starting document, with the following command:
create query
Creates a new ScandiPWA query document to work with Queries:
Syntax:
create query <name>
name
is the name of the query to be created.
Example:
scandipwa create query Weather
# Output:
NOTE!
The following files have been created:
src/query/Weather.query.js
Tutorial: Add additional fields to a query using plugins
Tutorial: How to debug a GraphQL Query
Tutorial: How to increase Query Complexity limits
NAME