GraphQL Queries
GraphQL queries are generated dynamically using javascript
Last updated
Was this helpful?
GraphQL queries are generated dynamically using javascript
Last updated
Was this helpful?
Magento exposes a 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 to get a full understanding of the language, but we do provide 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:
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:
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.
ScandiPWA implements its own library to enable you to generate GraphQL queries and mutations. This is defined in util/Query
.
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.
Resulting Query (argument valus are sent separately):
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
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 make
onSuccess(data, dispatch)
, a function that is called with the response data when the query completes successfully
onError(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).
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
.
When a query generation file is defined, it can be used to make requests in dispatchers as well as component's containers.
The solution ScandiPWA proposes is to generate queries dynamically. Then, adapting the query to your needs is as easy as using the on the query's class.
Represents a
Represents a . Extends Field, and hence has the same API.
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 file: