...
It's possible to only allow specific users access to the GraphQL. This will require a valid User with an application key. You can enable authentication in the Setup by enabling the require_authentication option. You can authenticate yourself with GraphQL by adding the appkey
query parameter, including your application key, to your request.
Caching
In R44 caching was added the GraphQL API to improve the responsiveness of the API, especially for environments with a lot of content. GraphQL caches the results for the time set in the cache_expiration_time
option in the Setup. The cache does not automatically invalidate when the source has been changed. This means that changes will can take some time to propegate to the GraphQL API.
...
The inline documentation of the API can be requested via an Introspection query. This query always requires user authentication, so you have to provide a valid appkey
query parameter with the request. Clients like Postman or Insomnia can turn the result of this query into a readable schema. Using Insomnia, you can click the schema button and the select "Show documentation". This opens a popup containing the API's documentation. In Postman you can change the request type from HTTP to GraphQL and the schema should automatically load into the Query tab.
...
Below is a sample query where we are filtering and sorting on the Source property, which has a combined search index, and the Author property of the Blog modular template which has a unique search index.
...
theme | Eclipse |
---|
...
This sample query also shows how to return Modular Content fields. There are three places where Modular Content fields can be used, namely in Modular Content Items, in Page Metadata, and in Modular Elements.
Code Block | ||
---|---|---|
| ||
query FilteredItems { filteredItems( filter: { modularSource: { or: ["Autoweek"] } modularBlogAuthor: { not: "Steven" } } sort: [ {field: modularSource, order: desc}, {field: modularBlogAuthor, order: asc} ] first: 5 language: nl_NL ) { edges { node { id title }... on BlogModule { } } } |
XperienCentral GraphQL Documentation
The inline documentation of the API can be explored using one of the REST clients mentioned above. Using Insomnia, you can click the schema button and the select "Show documentation". This opens a popup containing the API's documentation.
Extending the API
Any change made to XpereienCentral that affects the GraphQL schema requires the schema to be fully regenerated. This includes, but is not limited to, enabling/disabling a Content Type or making a change to a Modular Content Template. The schema is regenerated automatically upon receiving the first query after the change that affected the schema was made.
Adding custom queries
To add new queries to the API we have to create a new service which implements the GraphQLDeliveryApi.Provider
. A query can be added by creating a public method with the GraphQL SPQR annotations including the @GraphQLQuery
annotation.
Example
For this example let's start by creating a Service component using the Quick Start guide. This will generate a plugin for you with a service component. This will provide you with the following files in the com.gxwebmanager.helloworld.helloworldservice folder.
Activator.java
api/
package.html
HelloWorldServiceService.java
WCBConstants.java
service/
package.html
HelloWorldServiceServiceImpl.java
Now we're going to edit Activator.java
and HelloWorldServiceServiceImpl.java
. In order to be picked up by the GraphQL API the HelloWorldServiceServiceImpl.java
needs to implement the GraphQLDeliveryApi.Provider
interface. In addition any methods that should be exposed in the API need to be annotated using the GraphQL SPQR annotations. The file below contains a complete example of the file.
HelloWorldServiceServiceImpl.java
Code Block | ||
---|---|---|
| ||
package com.gxwebmanager.helloworld.helloworldservice; import com.gxwebmanager.helloworld.helloworldservice.api.HelloWorldServiceService; import io.leangen.graphql.annotations.GraphQLQuery; import nl.gx.webmanager.wcb.servicetype.impl.SimpleServiceComponent; import nl.gx.webmanager.wcbs.graphqlservice.api.GraphQLDeliveryApi; import java.text.DateFormat; import java.util.Date; /** * Implementation of the HelloWorldService service component. */ public class HelloWorldServiceServiceImpl extends SimpleServiceComponent implements GraphQLDeliveryApi.Provider, HelloWorldServiceService { /** * {@inheritDoc} */ @GraphQLQuery(name = "timestamp", description = "Returns the current timestamp of the server.") public String getTimeStamp() { properties { author source } } ... on Page { pageMetaData { ... on ModularPageMetadata { blog { author source } } } content { elements { return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date()); ... on ModularElement { } } |
In addition the Activator also needs to be updated. The following line needs to be updated from:
Code Block | ||
---|---|---|
| ||
definition.setInterfaceClassNames(new String[]{SimpleServiceComponent.class.getName(), HelloWorldServiceService.class.getName()}); |
to
Code Block | ||
---|---|---|
| ||
definition.setInterfaceClassNames(new String[]{SimpleServiceComponent.class.getName(), GraphQLDeliveryApi.Provider.class.getName(), HelloWorldServiceService.class.getName()}); |
Now build the plugin and deploy it on your environment. The timestamp query will automatically be registered to the GraphQL API and should show up in your schema after a refresh.
Adding GraphQL Support to Custom Elements/Content Items/Form Fragments/Page Metadata
Custom elements, content items, form fragments and page metadata can also be returned by the GraphQL API. However this does require some additional code. If the additional code is not provided, GraphQL can still return these items but only via a generic query which can only include default fields.
Pojo Classes
In R44 we redesigned our GraphQL API implementation to use Pojo classes. This was done to simplify our implementation, make the code clearer and more readable by separating GraphQL from the rest of the code and improve the performance of the GraphQL API by caching the requested content in Pojos.
The data we expose via the GraphQL API is now stored in and accessed via these Pojo classes. This does give developers more control over what and how the data should be exposed via the GraphQL API without requiring any changes to the rest of the application.
When creating a Pojo class, there are a few important things to keep in mind. First, all data must be set creating the Pojo which means that all class variables should be set to final. Second, variables of a custom class must return the Pojo of that custom class. A lot of default XPerienCentral classes already have a Pojo which can be reused. Checkout the Javadoc for more information.
Pojo Annotation
Example
For this example we create a new custom Element named GraphQLTest (see the Quick Start guide for more details).
There are only three relevant files for us, namely:
api/GraphQLTestElement.java
is the interface in which we define the methods of our custom element.api/GraphQLTestElementPojo.java
is the Pojo file for our element, which contains the methods for the fields we want to expose via GraphQL.element/GraphQLTestElementImpl.java
is the file which contains the implementation of the interface and is contains the class where the@POJO
annotation is added.
...
blog {
author
source
}
}
}
}
}
}
}
}
} |
...
XperienCentral GraphQL Documentation
The inline documentation of the API can be explored using one of the REST clients mentioned above. Using Insomnia, you can click the schema button and the select "Show documentation". This opens a popup containing the API's documentation.
...
Extending the API
Any change made to XpereienCentral that affects the GraphQL schema requires the schema to be fully regenerated. This includes, but is not limited to, enabling/disabling a Content Type or making a change to a Modular Content Template. The schema is regenerated automatically upon receiving the first query after the change that affected the schema was made.
Adding Custom Queries
To add new queries to the API we have to create a new service which implements the GraphQLDeliveryApi.Provider
. A query can be added by creating a public method with the GraphQL SPQR annotations including the @GraphQLQuery
annotation.
Example
For this example let's start by creating a Service component using the Quick Start guide. This will generate a plugin for you with a service component. This will provide you with the following files in the com.gxwebmanager.helloworld.helloworldservice folder.
Activator.java
api/
package.html
HelloWorldServiceService.java
WCBConstants.java
service/
package.html
HelloWorldServiceServiceImpl.java
Now we're going to edit Activator.java
and HelloWorldServiceServiceImpl.java
. In order to be picked up by the GraphQL API the HelloWorldServiceServiceImpl.java
needs to implement the GraphQLDeliveryApi.Provider
interface. In addition any methods that should be exposed in the API need to be annotated using the GraphQL SPQR
annotations. The file below contains a complete example of the file.
HelloWorldServiceServiceImpl.java
Code Block | ||
---|---|---|
| ||
package com.gxwebmanager.helloworld.helloworldservice;
import com.gxwebmanager.helloworld.helloworldservice.api.HelloWorldServiceService;
import io.leangen.graphql.annotations.GraphQLQuery;
import nl.gx.webmanager.wcb.servicetype.impl.SimpleServiceComponent;
import nl.gx.webmanager.wcbs.graphqlservice.api.GraphQLDeliveryApi;
import java.text.DateFormat;
import java.util.Date;
/**
* Implementation of the HelloWorldService service component.
*/
public class HelloWorldServiceServiceImpl extends SimpleServiceComponent implements GraphQLDeliveryApi.Provider, HelloWorldServiceService {
/**
* {@inheritDoc}
*/
@GraphQLQuery(name = "timestamp", description = "Returns the current timestamp of the server.")
public String getTimeStamp() {
return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date());
}
} |
In addition the Activator also needs to be updated. The following line needs to be updated from:
Code Block | ||
---|---|---|
| ||
definition.setInterfaceClassNames(new String[]{SimpleServiceComponent.class.getName(), HelloWorldServiceService.class.getName()}); |
to
Code Block | ||
---|---|---|
| ||
definition.setInterfaceClassNames(new String[]{SimpleServiceComponent.class.getName(), GraphQLDeliveryApi.Provider.class.getName(), HelloWorldServiceService.class.getName()}); |
Now build the plugin and deploy it on your environment. The timestamp query will automatically be registered to the GraphQL API and should show up in your schema after a refresh.
...
Adding GraphQL Support to Custom Elements/Content Items/Form Fragments/Page Metadata
Custom elements, content items, form fragments and page metadata can also be returned by the GraphQL API. However this does require some additional code. If the additional code is not provided, GraphQL can still return these items but only via a generic query which can only include default fields.
Pojo Classes
In R44 we redesigned our GraphQL API implementation to use Pojo classes. This was done to simplify our implementation, make the code clearer and more readable by separating GraphQL from the rest of the code and improve the performance of the GraphQL API by caching the requested content in Pojos.
The data we expose via the GraphQL API is now stored in and accessed via these Pojo classes. This does give developers more control over what and how the data should be exposed via the GraphQL API without requiring any changes to the rest of the application.
When creating a Pojo class, there are a few important things to keep in mind. First, all data must be set creating the Pojo which means that all class variables should be set to final. Second, variables of a custom class must return the Pojo of that custom class. A lot of default XPerienCentral classes already have a Pojo which can be reused. Checkout the Javadoc for more information.
Pojo Annotation
Example
For this example we create a new custom Element named GraphQLTest (see the Quick Start guide for more details).
There are only three relevant files for us, namely:
api/GraphQLTestElement.java
is the interface in which we define the methods of our custom element.api/GraphQLTestElementPojo.java
is the Pojo file for our element, which contains the methods for the fields we want to expose via GraphQL.element/GraphQLTestElementImpl.java
is the file which contains the implementation of the interface and is contains the class where the@POJO
annotation is added.
Let’s add a method to our custom element, for example the method String getFavoriteClient()
which returns ones favorite GraphQL client. After adding the method to our interface and implementation, we also have to add it to our Pojo (if we want to expose it via the GraphQL API that is, if not we don’t have to do anything and it won’t be exposed).
Code Block | ||
---|---|---|
| ||
public interface Browser {
public String getName();
public String getVersion();
} |
After creating the Browser implementation, adding the Browser
getBrowser()
method to our interface and implementation like we normally do, we now also have to create a new Pojo for the Browser
interface, which we will name BrowserPojo, and add the browser variable to the GraphQLTestElementPojo
(if we want to expose the variable via the GraphQL API that is, if we don’t want to expose it we don’t have to do anything, since that is the default behavior).
We start by creating the BrowserPojo
class. This is the class that will be used by GraphQL and thus requires the GraphQL annotations. Below is the sample code for the BrowserPojo
class.
Code Block | ||
---|---|---|
| ||
@GraphQLType(name = "Browser", description = "An object representing a Browser.")
public class BrowserPojo {
private final String name;
private final String version;
public BrowserPojo(Browser browser) {
this.name = browser.getName();
this.version browser.getVersion();
}
@GraphQLQuery(description = "Returns the name of the browser.")
public String getName() {
return name;
}
@GraphQLQuery(description = "Returns the version of the browser.")
public String getVersion() {
return version;
}
}
|
Next we will have to make three changes to the GraphQLTestElementPojo
class:
First we have to create a new class variable where we can store our favorite client in (so it can be cached):
private final BrowserPojo browser
;Next we have to set the value of this variable in the constructor, which usually is just calling a getter, but in this case we have to convert the browser to a
BrowserPojo
and make sure it’s not NULL to prevent a NullPointerException from being thrown. So we would add the following line to the constructor:Code Block theme Eclipse this.browser = mediaItemVersion.getBrowser() != null ? new BrowserPojo(mediaItemVersion.getBrowser()) : null;
Lastly we also have to add a new getter including the
@GraphQLQuery
annotation so the browser can be requested via the GraphQL API:Code Block theme Eclipse @GraphQLQuery(description = "Returns the browser of the media item.") public BrowserPojo getBrowser() { return browser; }
And that’s it. We have now added support for a new custom element to our GraphQL API including a new method with a new class definition. This process is identical for custom Media Items, Page Metadata and Form Fragments. The only difference is the base Pojo class which our custom Pojo classes extend from. These being MediaItemArticleVersionPojo for custom Media Items, FormFragmentPojo for custom Form Fragments, PageMetaDataPojo for custom Page Metadata and ElementPojo form custom Elements.