GraphQL Queries
GraphQL queries are generated dynamically using javascript
Magento exposes a GraphQL API, which ScandiPWA builds upon and uses to fetch and mutate data. GraphQL is more flexible than REST, and offers a type checking system.
You might want to read the GraphQL documentation to get a full understanding of the language, but we do provide a quick introduction below.
Introduction to GraphQL
GraphQL queries look like JSON objects without quotes or values, and describe which fields you want to fetch from the server:
In this case, we want to query the categoryList
field, and fetch its name and children fields. For the children, we 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 children
is actually an array of such objects.
If we make the above request to the GraphQL endpoint on a Magento instance (it's usually /graphql
), we would get the following JSON response:
The categoryList
query has other fields that we could fetch (such as id
, description
), but GraphQL only returns the ones we asked for.
API
Generating GraphQL Queries
Commonly, when building an application, GraphQL queries are specified in the code as strings, much like the query above. While this method does have its advantages, it is hard to extend - what happens when you want to override a theme and need to fetch one more field from the same query? If queries were hardcoded as strings, you would have to copy and edit the string to create a new query in your theme, which would quickly get hard to maintain. And what about plugins? There would be no easy way to write a plugin that asks for an additional field in some query.
The solution ScandiPWA proposes is to generate queries dynamically. Then, adapting the query to your needs is as easy as using the Override Mechanism on the query's class.
ScandiPWA implements its own library to enable you to generate GraphQL queries and mutations. This is defined in util/Query
.
Field
Represents a GraphQL field
Method
Description
addField(
field: Field|string
) -> this
Adds the specified child field to this field as a subfield. The child field must be either an instance of Field, or a string. If a string is specified, this is equivalent to calling addField(new Field(string))
addFieldList(
fields:(Field|string)[]
) -> this
Adds the specified list of child fields to this field as subfields. Each item of fields
must be either an instance of Field, or a string. If a string is specified, this is equivalent to passing new Field(string)
addArgument(
name: string,
type: string,
value: string
) -> this
Adds an argument called name
with a value of value
of type type
to this field. All three parameters must be strings, and are required.
setAlias(
alias: string
) -> this
Aliases the field to the specified alias.
Fragment
Represents a GraphQL fragment. Extends Field, and hence has the same API.
Example
Resulting Query (argument valus are sent separately):
Making Queries
Functions that make requests are defined in util/Request
. These implement a smart caching mechanism to improve performance and reduce load.
Function
Description
fetchQuery(
query: Field|Field[]
) -> Promise<Request>
Fetches a query or array of queries, and returns a Promise that resolves when the query successfully completes. If the query fails, so does the Promise.
fetchMutation(
query: Field|Field[]
) -> Promise<Request>
Fetches a mutation or array of mutations, and returns a Promise that resolves when the mutation successfully completes. If the mutation fails, so does the Promise.
QueryDispatcher
QueryDispatcher
is a base class for redux dispatchers that can simplify making queries. Dispatchers that need to make queries typically need to make the request, and then handle the resulting data or any errors. QueryDispatcher
automates this this by implementing a handleData
function that performs this logic.
A subclass extending QueryDispatcher
will need to define 3 functions. Once these 3 functions are defined, handleData
will work automatically as expected.
prepareRequest(options, dispatch)
, a function that returns the query that the dispatcher wants to makeonSuccess(data, dispatch)
, a function that is called with the response data when the query completes successfullyonError(error, dispatch)
, a function that is called on request error
Note that all 3 functions get access to Redux's dispatch
function in case they need to use it (e.g. to show a notification).
GraphQL in ScandiPWA
To keep the codebase organized, we don't want the components or redux dispatchers to be responsible for generating queries. Instead, we keep all query-generating code in query
.
By convention, query
contains 1 JavaScript file for each group of related queries. For example, the Cart.query.js
file contains queries relating to querying or mutating the customer's cart. All of these files define classes with one or more functions to generate some query, as well as "private" helper methods (optionally). These classes are exported as singleton instances intended to be used by the rest of the app. For an example, consider the Cart.query.js
file:
When a query generation file is defined, it can be used to make requests in dispatchers as well as component's containers.
Last updated