GraphQL
Frontastic supports GraphQL to query backend systems so that you can integrate any GraphQL based API with Frontastic. We don't limit usage or datasets in any way. This is so you can integrate any concept you want to and merge the data from your APIs with the data from the commerce or content APIs.
Using data stream configurations you can allow users to adapt or even write GraphQL queries in the Frontastic studio. We'll go through some examples for this in this article.
Frontastic doesn't allow you to query its data partially from the frontend using GraphQL. The main reason for this is, that users in the Frontastic studio define what data should be shown on a certain page. The Frontastic <glossary:API hub>> then ensures that the correct data is fetched accordingly to the configuration. The main interface between developer and business user is the Frontastic components and our minimal frontend framework provides your components automatically with all the data needed, as configured in the Frontastic studio.
This means: If there's a certain frontend component on a page and it's currently visible, depending on the device, then Frontastic will fetch the data from the GraphQL backend automatically. And this can depend on context, like the current user, query parameters in the URL, and, the configuration from the Frontastic studio.
1. Create a Frontastic component
To be able to visualize the data coming out of the GraphQL data source, we need to create a Frontastic component.
For this example, we'll be using the Frontastic CLI, our name for the component will be streamExample
, and the custom data source will be example/graphql
.
- Run
frontastic generate tastic
in the Frontastic CLI and enter the name of your new Frontastic component
frontastic generate tastic
? Please enter a name for the new Tastic streamExample
- Edit the schema file to have a section like the below which includes the streamType (you can also add any other configuration options, but we'll focus on the custom data source here)
{
"tasticType": "show/streamExample",
"name": "Stream Example",
"icon": "source",
"category": "general",
"schema": [
{
"name": "The Basics",
"fields": [
{
"label": "Data Source",
"field": "dataSource",
"type": "stream",
"streamType": "example/graphql"
}
]
}
]
}
-
Add the schema to the Frontastic studio (see the Frontastic components article for more information)
-
Update the JSX file to display the raw data from the custom data source (we'll view the query results in this example)
import React from 'react'
import PropTypes from 'prop-types'
function StreamExampleTastic ({ data }) {
return <pre>{JSON.stringify(data.dataSource || 'No data yet', null, 2)}</pre>
}
StreamExampleTastic.propTypes = { data: PropTypes.object.isRequired }
export default StreamExampleTastic
Now we have the Frontastic component updated, we need to create a custom stream handler.
2. Create a custom data source handler
To be able to use the data from our custom data source, we need to create a custom data source handler (also known as the tasticFieldHandler).
- Create a new bundle using the below command (we're using
Test
for this example)
bin/console frontastic:create:bundle -v Test
See the creating a backend bundle article for more information on bundles.
-
Open the
services.xml
file in the new bundle folder (for example,<customer>/<project>/src/php/TestBundle/Resources/config/services.xml
) -
Uncomment the section in the created
services.xml
and register the custom data source using thetasticFieldHandler
(see our TasticFieldHandler article for more information)
<service id="Show\TestBundle\Domain\GraphQLStreamHandler">
<tag name="frontend.tasticFieldHandler"/>
</service>
We're using a tag
for now but we'll add additional service parameters later.
- Create a PHP class
The string returned by
getType
must match the name of the custom data source in thetastic.json
we created in step 2 of the 1st section.
namespace Show\TestBundle\Domain;
use Frontastic\Catwalk\FrontendBundle\Domain\TasticFieldHandlerV3;
use Frontastic\Catwalk\ApiCoreBundle\Domain\Context;
use Frontastic\Catwalk\FrontendBundle\Domain\Node;
use Frontastic\Catwalk\FrontendBundle\Domain\Page;
use Frontastic\Catwalk\FrontendBundle\Domain\Tastic;
class GraphQLStreamHandler extends TasticFieldHandlerV3
{
public function getType(): string
{
return 'example/graphql';
}
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
return 'data';
}
}
The handle
method can return anything, which will then be passed to the Frontastic component. You need to make sure that the returned data is handled in the Frontastic component or you could map or filter the data in the custom data source handler.
- Query a GraphQL endpoint
In our example, we'll use the Frontastic HTTP client using the service name Frontastic\Common\HttpClient
and have the services.xml
inject it into our custom data source handler. We'll be querying the public Star Wars API GraphQL endpoint available under https://graphql.org/swapi-graphql.
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
return json_decode($this->httpClient->request(
'POST',
'https://swapi-graphql.netlify.app/.netlify/functions/index',
json_encode(['query' => '{ allFilms { films { title } } }']),
['Accept: application/json', 'Content-Type: application/json'],
)->body);
}
You can also use file_get_contents
or plain curl
to query the endpoint and you can modify the query if you want to. In the next section, we'll show you how to set up the ability to modify the query in the Frontastic studio.
3. Custom data source configuration
Now we have our Frontastic component and our custom data source handler, we can make it so the query is configurable in the Frontastic studio (see this section of the TasticFieldHandler article for more information).
To do this, we need to create a schema for the data source type which defines the configuration options in the page builder and make sure this configuration can be handled.
- Create a schema file similar to below
The
customStreamType
property must match thestreamType
defined in theschema.json
from step 2 in the 1st section.
{
"customStreamType": "example/graphql",
"name": "GraphQL",
"category": "Content",
"icon": "source",
"schema": [
{
"name": "Configuration",
"fields": [
{
"label": "Query",
"field": "query",
"type": "text",
"default": "",
"translatable": false
}
]
}
]
}
In this example, the full GraphQL query will be configurable.
- Update the
handle
method in the PHP class for your bundle so that the$fieldValue
variable matches thefield
in the schema
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
return json_decode($this->httpClient->request(
'POST',
'https://swapi-graphql.netlify.app/.netlify/functions/index',
json_encode(['query' => ($fieldValue['query'] ?? '{ allFilms { films { title } } }')]),
['Accept: application/json', 'Content-Type: application/json'],
)->body);
}
Now, the GraphQL query can be specified in the page builder of the Frontastic studio. You could even make it so it can specify the URL and maybe access credentials and you would have a generic GraphQL data source:

This works when just displaying raw data, but in a real project, your Frontastic component will expect a certain data structure and so won't work with random queries. You'll probably only want to make certain parameters in the query configurable, let's look at how to do that.
commercetools and GraphQL
If you're using commercetools as your product API, we can use the already authorized commercetools client to request data using the commercetools GraphQL API (see the commercetools GraphQL API documentation for more information).
The following example will only work in projects where the product API uses commercetools.
- Define the stream handler in the
services.xml
in your newly created bundle
<service id="CommercetoolsClient" class="Frontastic\Common\ProductApiBundle\Domain\ProductApi\Commercetools\Client">
<factory service="frontastic.catwalk.product_api"
method="getDangerousInnerClient"
/>
</service>
<service id="Show\TestBundle\Domain\CommercetoolsStreamHandler">
<argument type="service" id="CommercetoolsClient" />
<tag name="frontend.tasticFieldHandler"/>
</service>
This will pass our already authenticated commercetools client to the custom data source handler we created earlier.
- Use the
request
method of the commercetools client to run a GraphQL query against the space in commercetools which has been configured for your project
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
return $this->commercetools->request(
'POST', '/graphql', [], [],
json_encode([
'query' => 'query ($sku: String!) { product(sku: $sku) { id, version } }',
'variables' => [
'sku' => $fieldValue['sku'] ?? 'M0E20000000ED0W',
]
]),
)->wait();
}
In this example, we use the $fieldValue['sku']
which references a sku
configuration field from the custom data source schema. This means that the SKU of the product can be configured in the Frontastic studio.
Since we query the id
and version
of products, the output of the Frontastic component will just contain the following. So, you can fetch exactly the data you need using this mechanism.
{
"data": {
"product": {
"id": "8d2f35a1-0386-4ca4-9dfc-9501999386c5",
"version": 1348
}
}
}
Query parameters
We can also combine the configuration from the Frontastic studio with request parameters if we want to. In the following example, we'll check if the SKU is defined as a query parameter (/myPage?sku=S0MESKU
). If this parameter exists we'll read the SKU from the query parameter. If not, we fall back to the value configured by the business user. If that configuration is also empty we just return null
. The logic might be different for your case, but this allows for loading different data based on user interaction, too.
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
$sku = $_GET['sku'] ?? $fieldValue['sku'] ?? null;
if (!$sku) {
return null;
}
return $this->commercetools->request(
'POST', '/graphql', [], [],
json_encode([
'query' => 'query ($sku: String!) { product(sku: $sku) { id, version } }',
'variables' => ['sku' => $sku],
]),
)->wait();
}
GraphCMS and GraphQL
If you're using commercetools as your product API, we can use the already authorized GraphCMS client to request data using the GraphCMS GraphQL API (see the GraphCMS GraphQL API documentation.
The following example will only work in projects where the product API uses GraphCMS.
- Define the stream handler in the
services.xml
in your newly created bundle
<service id="GraphcmsClient" class="Frontastic\Common\ContentApiBundle\Domain\ContentApi\GraphCMS\Client">
<factory service="frontastic.catwalk.content_api"
method="getDangerousInnerClient"
/>
</service>
<service id="Show\TestBundle\Domain\GraphcmsStreamHandler">
<argument type="service" id="GraphcmsClient" />
<tag name="frontend.tasticFieldHandler"/>
</service>
This will pass our already authenticated GraphCMS client to the custom data source handler we created earlier.
- Use the
query
method of the GraphCMS client to run any GraphQL query against GraphCMS
public function handle(Context $context, Node $node, Page $page, Tastic $tastic, $fieldValue)
{
return json_decode($this->graphcms->query(
'{ products (where: { price_gt: 45 }) { id, name, price } }'
)->wait());
}
You'll see the implementation of our custom data source handler looks even simpler in this case. Our example space contains products with the schema name Product
so we can query for all products with a price higher than 45
. The sensible queries highly depend on your content schema and values like the 45
should be made configurable by business users – see the earlier examples for a more detailed description of how to do this.
This query will then give exactly the content elements you requested with only the properties you need in the frontend:
{
"data": {
"products": [
{
"id": "ckdu44mn40gxh010405uwgbtw",
"name": "Unisex Long Sleeve Tee",
"price": 100
},
{
"id": "ckdu48unc0gzq0158mbzvyzg3",
"name": "Snapback",
"price": 100
},
/* … */
]
}
}
Updated over 2 years ago