A Kentico Kontent data source plugin for Gridsome that aims to support all of the main features of Kentico Kontent:
✔ Content (including all content element types)
✔ Taxonomy
✔ Assets
The plugin also provides additional features and extension points to ease working with your Kentico Kontent content in Gridsome. Please keep reading to learn about:
- How to get started with this plugin
- The object types and object models that are added to the Gridsome GraphQL schema by this plugin
- How to render Rich Text fields using Vue single file components that you define in your app
- How to customise routing of content and taxonomy objects
- How to work with Taxonomy in Gridsome
- How to work with Assets in Gridsome, and how to transform Asset URLs directly in your GraphQL queries
- How to create content models to allow you to customise how content from Kentico Kontent is represented as data in Gridsome
- The full list of plugin configuration options
This getting started guide assumes that you have an existing Gridsome project, and that you want to add Kentico Kontent as a data source using this plugin. If you haven't yet created a Gridsome project, please follow the Gridsome getting started guide first and then come back here.
Use your preferred package manager to add a dependency on @meeg/gridsome-source-kentico-kontent to your Gridsome app, for example:
yarn add @meeg/gridsome-source-kentico-kontentnpm install @meeg/gridsome-source-kentico-kontent
Add @meeg/gridsome-source-kentico-kontent to the plugins array in your gridsome.config.js file, and configure the Kentico Kontent delivery client to fetch data from your project by specifying your project id in the plugin options:
plugins: [
{
use: '@meeg/gridsome-source-kentico-kontent',
options: {
deliveryClientConfig: {
projectId: process.env.KENTICO_KONTENT_PROJECT_ID
}
}
}
]The above configuration assumes that you are using environment variables to manage parts of your project configuration that you want to keep private (or that can vary in different environments), but you could specify the project id directly in the plugin options if you want.
See the Gridsome docs for general advice on installing plugins and using environment variables.
🙋 This is the minimum configuration required for the plugin to function. Please see the configuration section for a complete list of all options available.
First, configure Gridsome to use the "Runtime + Compiler" build of Vue because we will need the compiler to enable us to treat the Rich Text field content as a Vue component template.
In your gridsome.config.js file:
module.exports = {
...
runtimeCompiler: true
...
}Add a Vue single file component that will be used to render Rich Text fields. This component is a wrapper around v-runtime-template and will be extended to resolve other components embedded inside your Rich Text fields e.g. content components/items, content links and assets.
Create a new .vue file under your app components directory e.g. src/components/RichText.vue:
<template>
<v-runtime-template :template="html" />
</template>
<script>
import VRuntimeTemplate from 'v-runtime-template';
export default {
components: {
VRuntimeTemplate
},
props: {
html: {
type: String,
required: true
}
},
methods: {
getNode: function(codename, id) {
const query = this.$static[codename];
if (typeof(query) === 'undefined') {
return null;
}
const edges = query.edges.filter(
edge => edge.node.id === id
);
if (edges.length === 1) {
return edges[0].node;
}
return null;
}
}
};
</script>🙋 To learn how to extend this component to render content components/items, content links and assets (and how to opt-out of this approach if you don't like it), please see the section on rendering Rich Text fields.
From this point on you are ready to work with your Kentico Kontent content as data in Gridsome 😎
If you are new to Gridsome, the following areas of the docs should help you get up and running with data:
Remember to use the GraphQL explorer to explore your schema, and test queries when in development mode.
🙋 Now we've covered the basics, please keep reading for a more in-depth discussion of the various features of this plugin.
The following types of data are sourced from Kentico Kontent and made available for querying via the Gridsome GraphQL data store:
Content is available by querying against object types named using the codename of the content type they belong to converted to pascal case. For example:
- Given the codename
article, the object type will be namedArticle - Given the codename
landing_page, the object type will be namedLandingPage
🙋 See the section on configuration for options on how to customise naming of content object types.
Every content object type shares a core set of fields that include:
- System fields provided by the Kentico Kontent delivery client
- Fields required by this plugin
- Fields required by Gridsome
As such, every content object has at least the following fields:
| Name | Type | Notes |
|---|---|---|
id |
String |
Kentico Kontent's id is used as the object's id |
name |
String |
The name of the content item in Kentico Kontent |
codename |
String |
The codename of the content item in Kentico Kontent |
languageCode |
String |
The language codename of the content item |
type |
String |
The codename of the content type in Kentico Kontent that this content item belongs to |
typeName |
String |
The GraphQL object type name |
isComponent |
Boolean |
true if this object represents a content component/item; otherwise false - see the section on content component objects for further details |
date |
Date |
This is equal to the Kentico Kontent last_modified date, but is named date because that is the convention in Gridsome |
slug |
String |
This is set to the value of the "URL slug" content element if one is defined on the content type that this content belongs to; otherwise the name system field is slugified |
path |
String |
This is the path generated by Gridsome and is based on the route defined for this object type; this field will be undefined if no route has been specified |
As well as system fields, each object type contains fields that represent each of the content elements of the Kentico Kontent content type that the content belongs to.
These fields are named using the codename of the corresponding content element converted to camel case. For example:
- Given the codename
title, the field will be namedtitle - Given the codename
page_metadata_meta_title, the field will be namedpageMetadataMetaTitle
If there is a collision of field name a positive auto-incremented integer will be added as a suffix to the field name.
For example, a Kentico Kontent content type has a content element with the codename
datethat will collide with the "system"datefield so it will receive the field namedate1when added to the corresponding object type in the GraphQL schema.
All types of content element available in Kentico Kontent are supported and are represented in the object type definition as fields:
| Content element | Type | Notes |
|---|---|---|
| Text | String |
Text elements are represented as string fields |
| Rich text | String |
Rich text elements contain HTML markup, but are represented as strings - see the section on rendering Rich Text fields for details of how to render Rich Text fields in your Gridsome app |
| Number | Number |
Number elements are represented as numbers |
| Multiple choice | Object |
Multiple choice elements are represented as objects containing two properties: name, and codename |
| Date & time | Date |
Date & time elements are represented as dates |
| Asset | Asset[] |
Asset elements contain an array of references to asset objects |
| Linked items | <Content>[] |
Linked items elements contain an array of references to the content objects they are linked to - the plugin assumes that the objects are all of the same object type |
| Custom element | String |
Custom elements are represented as strings |
| Taxonomy | <Taxonomy>[] |
Taxonomy elements contain an array of references to taxonomy objects belonging to the relevant taxonomy object type |
| URL slug | String |
URL slug elements are represented as strings - they are regarded as a system field and will always be assigned to a field called slug, if present |
🙋 See the section on creating content models for details on how you can customise how content elements are translated to content object fields.
Content components and content items used in Rich Text fields are also added to object types in the Gridsome GraphQL schema.
The difference between content component objects and "regular" content objects is that content component objects are components of larger pieces of content and therefore not intended to be used in isolation i.e. have their own Gridsome template.
The isComponent system field can be used to filter content component objects in content queries if required.
Item links are primarily used when rendering Rich Text fields so feel free to skip this section unless you really want to read it! 🤓
When editing content inside Rich Text elements in Kentico Kontent you can add links to other content items within your Kentico Kontent project. To resolve these links within your Gridsome app, the path of the content item that has been linked to must be used as the URL of the link.
Gridsome generates the path value for an object (based on a defined route) when it is inserted into the GraphQL data store via the Data Store API.
To get the path of an object in the GraphQL data store you must either:
- Already have a reference to the relevant content object; or
- Know the
idandtypeNameof the content item that has been linked to, and form a GraphQL query to fetch an object with theidfrom the object type that corresponds to thetypeName
If neither of the above are true, the ItemLink object type can be queried using only the id of the content item that has been linked to to find the path of the corresponding content object.
The ItemLink object type has these fields:
| Name | Type | Notes |
|---|---|---|
id |
String | Kentico Kontent's id and the id of the corresponding content object |
typeName |
String | The GraphQL object type name of the corresponding content object |
path |
String | The path of the corresponding content object |
🙋 See the section on configuration for options on how to customise naming of the ItemLink object type.
Taxonomy terms are available by querying against object types named using the codename of the taxonomy group they belong to converted to pascal case, and prefixed with "Taxonomy". For example:
- Given the taxonomy group codename
tag, the object type will be namedTaxonomyTag - Given the taxonomy group codename
article_topics, the object type will be namedTaxonomyArticleTopics
🙋 See the section on configuration for options on how to customise naming of taxonomy object types.
Taxonomy term object types share a core set of fields that include:
- System fields provided by the Kentico Kontent delivery client
- Fields required by this plugin
- Fields required by Gridsome
As such, every taxonomy term object has the following fields:
| Name | Type | Notes |
|---|---|---|
id |
String |
The codename of the taxonomy term in Kentico Kontent is used as the object's id |
name |
String |
The name of the taxonomy term in Kentico Kontent |
slug |
String |
A slug is generated so that it can be used when specifying routes for object types |
terms |
<Taxonomy>[] |
Taxonomy terms can have child terms, which are an array of references to taxonomy objects |
path |
String |
If a route has been specified for this taxonomy object type Gridsome will generate a path for each object belonging to that type; otherwise the path will be undefined |
🙋 For some examples of how taxonomy can be used in Gridsome, please see the section on working with taxonomy.
Assets are available by querying against an object type named Asset.
🙋 See the section on configuration for options on how to customise naming of the asset object type.
Every asset object has the following fields provided by the Kentico Kontent delivery client:
| Name | Type | Notes |
|---|---|---|
id |
String |
The URL of the asset is used as the object's id |
type |
String |
MIME type of the asset |
size |
Number |
Size of the asset in bytes |
description |
String |
Description of the asset |
url |
String |
Absolute URL of the asset - this field accepts arguments allowing you to transform image URLs directly in your GraphQL queries |
width |
Number |
Width of the image in pixels, if the asset is an image |
height |
Number |
Height of the image in pixels, if the asset is an image |
🙋 For some examples of how assets can be used in Gridsome, please see the section on working with assets.
A Rich Text field is a string containing HTML markup, and that HTML markup can contain standard HTML elements as well as:
- Anchor links to other content that require resolving the link URL to the actual URL within your application
- Assets such as images that may require some flexibility in rendering (such as the use of lazy loading and/or
srcsetandsizes) - Custom elements that represent content components
As briefly touched on in the getting started guide, the recommended way to render Rich Text fields when using this plugin is to use a Vue single file component to represent a Rich Text field, which will:
- Compile the HTML markup as a dynamic template using
v-runtime-template; and - Allow you to write other Vue single file components to represent content components/items, content links and assets
The following sub-sections detail how to implement the above, and assume that you have already created the RichText component outlined in the getting started section.
First add a query to your RichText component inside a <static-query> block that will fetch all ItemLink objects. The alias for allItemLink must be item_link as shown below:
query RichText {
item_link: allItemLink {
edges {
node {
id,
path
}
}
}
}If you already have a
RichTextquery you can add theitem_linkalias and fields alongside other aliases.
Next you must create an ItemLink Vue single file component that has a node prop, and will render the link appropriately. The "shape" of the node prop object will match the node defined on the item_link query. The component must also have a slot via which the link text will be passed. For example:
<template>
<g-link :to="node.path">
<slot />
</g-link>
</template>
<script>
export default {
props: {
node: {
type: Object,
required: true
}
}
};
</script>Finally, you must add the ItemLink component to your RichText component:
<script>
import VRuntimeTemplate from 'v-runtime-template';
import ItemLink from '~/components/ItemLink.vue';
export default {
components: {
VRuntimeTemplate,
ItemLink
},
props: {
html: {
...
}
},
methods: {
getNode: function(codename, id) {
...
}
}
};
</script>Now if the Rich Text field HTML passed in the html prop of the RichText component contains any item-link components, those components have a node attribute that will call the getNode method with a codename of item_link, and an id that matches the content they are linking to. If an object with a matching id is found in the collection, it will be passed to the item-link component's node prop, otherwise null will be passed.
🙋 See the section on configuration for options on how to customise component names.
First add a query to your RichText component inside a <static-query> block that will fetch all Asset objects. The alias for allAsset must be asset as shown below:
query RichText {
asset: allAsset {
edges {
node {
id,
url(width: 1200, format: "webp"),
placeholderUrl: url(width: 50, format: "webp")
description
}
}
}
}If you already have a
RichTextquery you can add theassetalias and fields alongside other aliases.
Next you must create an Asset Vue single file component that has a node prop, and will render the asset appropriately. The "shape" of the node prop object will match the node defined on the asset query. For example:
<template>
<v-lazy-image
:src="node.url"
:src-placeholder="node.placeholderUrl"
:alt="node.description"
/>
</template>
<script>
import VLazyImage from 'v-lazy-image';
export default {
components: {
VLazyImage
},
props: {
node: {
type: Object,
required: true
}
}
};
</script>
<style scoped>
img {
width: 100%;
}
.v-lazy-image {
filter: blur(5px);
transition: filter 1.6s;
will-change: filter;
}
.v-lazy-image-loaded {
filter: blur(0);
}
</style>Unfortunately Gridsome's g-image component doesn't appear to work with
v-runtime-templateso the example above is using v-lazy-image, but you can try whatever you wish in your own component!
Finally, you must add the Asset component to your RichText component:
<script>
import VRuntimeTemplate from 'v-runtime-template';
import Asset from '~/components/Asset.vue';
export default {
components: {
VRuntimeTemplate,
Asset
},
props: {
html: {
...
}
},
methods: {
getNode: function(codename, id) {
...
}
}
};
</script>Now if the Rich Text field HTML passed in the html prop of the RichText component contains any asset components, those components have a node attribute that will call the getNode method with a codename of asset, and an id that matches the asset they represent. If an object with a matching id is found in the collection, it will be passed to the asset component's node prop, otherwise null will be passed.
🙋 See the section on configuration for options on how to customise component names.
First add a query to your RichText component inside a <static-query> block that will fetch all <Content> objects. The alias for all<Content> must match the codename of the Kentico Kontent content type that the <Content> belongs to:
In the following examples, we will create a component to render code snippets, so the
<Content>object type isCodeSnippetand content type codename iscode_snippet:
query RichText {
code_snippet: allCodeSnippet {
edges {
node {
id,
code,
language {
name
}
}
}
}
}If you already have a
RichTextquery you can add thecode_snippetalias and fields alongside other aliases.
Next you must create a CodeSnippet Vue single file component that has a node prop, and will render the asset appropriately. The "shape" of the node prop object will match the node defined on the code_snippet query. For example:
<template>
<pre class="code-snippet">{{ node.code }}</pre>
</template>
<script>
export default {
props: {
node: {
type: Object,
required: true
}
}
};
</script>Finally, you must add the CodeSnippet component to your RichText component:
<script>
import VRuntimeTemplate from 'v-runtime-template';
import CodeSnippet from '~/components/CodeSnippet.vue';
export default {
components: {
VRuntimeTemplate,
CodeSnippet
},
props: {
html: {
...
}
},
methods: {
getNode: function(codename, id) {
...
}
}
};
</script>Now if the Rich Text field HTML passed in the html prop of the RichText component contains any code-snippet components, those components have a node attribute that will call the getNode method with a codename of code_snippet, and an id that matches the code snippet they represent. If an object with a matching id is found in the collection, it will be passed to the code-snippet component's node prop, otherwise null will be passed.
🙋 See the section on configuration for options on how to customise component names.
Although the approach to Rich Text outlined above is recommended it may not be for everybody or every application so it is possible to "opt-out" by setting certain option values to null in the plugin configuration. The following options are all keys of contentItemCong.richText:
| Option key | Behaviour when set to null |
|---|---|
wrapperCssClass |
Prevents any transformation of Rich Text HTML elements to components |
itemLinkSelector |
Content links are not transformed to item-link components |
assetSelector |
Assets are not transformed to asset components |
componentSelector |
Content components/items are not transformed to <content> components |
For example:
plugins: [
{
use: '@meeg/gridsome-source-kentico-kontent',
options: {
...
contentItemConfig: {
...
richText: {
wrapperCssClass: null
}
...
}
...
}
}
}If you opt out of this approach you can use features of the Kentico Kontent delivery client to resolve content links and content components/items, but not assets. These features of the delivery client are exposed by this plugin when creating content models.
🙋 See the section on configuration for options on how to customise Rich Text transformation.
Routing in Gridsome is defined in the templates section of gridsome.config.js. All routing must be explicitly configured in your config - there is no default routing in place.
Templates can be used for all content types and taxonomy groups in your project.
Routes are not resolved in any particular order so you may wish to avoid setting a route such as
/:slugas this could take precedence over and conflict with other routes.
Any route permitted by Gridsome's templates feature can be used for content types, but a common route pattern would be:
/{codename}/:slug
Where:
codenameis the "slugified" codename of the Kentico Kontent content type that the content belongs to; andslugis the system field named "slug"
If you do not wish for a content type to have a route (and therefore no path i.e. to not represent a navigable page) then you can decline to specify a template for it in gridsome.config.js.
If you have changed how content item type names are generated through this plugin's configuration options, remember to use the actual type name of the object in Gridsome's GraphQL schema when defining your templates.
Taxonomy groups can be routed in the same way as content types using templates. A common route pattern for a taxonomy group would be:
/{codename}/:slug
Where:
codenameis the "slugified" codename of the taxonomy group; andslugis the taxonomy term field named "slug"
The slug field is added to each taxonomy term object by this plugin and is generated like so:
- The
codenameof the taxonomy term is slugified - If the term belongs to a "parent" term, the parent's slug is prepended to the current term's slug in the format
{parent-slug}/{slug}
If you do not wish for a taxonomy group to have a route (and therefore no path i.e. to not represent a navigable page) then you can decline to specify a template for it in gridsome.config.js.
If you have changed how taxonomy group names are generated through this plugin's configuration options, remember to use the actual type name of the object in Gridsome's GraphQL schema when defining your templates.
🙋 See the next section for some examples of how you can work with taxonomy in Gridsome.
The Gridsome documentation describes how to create a taxonomy page template to display a Tag object and the Post objects that reference that Tag.
To achieve this with the Kentico Kontent plugin you will need to ensure that you first set up routing for the taxonomy group that you want to list (i.e. your equivalent of Tag from the Gridsome example), and then you can follow along with the documented approach.
The plugin will take care of adding the required references between Taxonomy objects (i.e. your equivalent of
Tagfrom the Gridsome example) and Content objects (i.e. your equivalent ofPostfrom the Gridsome example) via any Taxonomy content elements defined on the the Content object's content type in Kentico Kontent.
Below are a couple of examples of other things you can do with Taxonomy in Gridsome.
To do the inverse of what is documented and create a template to display a Post object and the Tag objects that the Post references (for example to list tags for a post), you can use a query like the below:
query Post ($id: String!) {
post (id: $id) {
title,
tags {
name,
path
}
}
}If you wanted to list all Tag objects including a count of how many Post objects reference each tag (for example in a "tag cloud" component), you can use a query like the below:
query Tags {
tags: allTag {
edges {
node {
name,
path,
belongsTo {
totalCount
}
}
}
}
}Assets are represented by the Asset object type in the Gridsome GraphQL schema and working with assets is largely the same as working with any other object type. The only "special" thing about Asset objects is that the url field accepts arguments that allow you to specify image transformations via a custom field resolver that uses the ImageUrlBuilder class provided by the Kentico Kontent delivery client.
The arguments accepted are (omit any of the arguments if you do not wish to use it):
| Name | Type | Equivalent ImageUrlBuilder function name |
Notes |
|---|---|---|---|
width |
Int |
withWidth |
Specify the desired width in pixels |
height |
Int |
withHeight |
Specify the desired height in pixels |
automaticFormat |
Boolean |
withAutomaticFormat |
true if you wish to automatically use webp format, and fallback to the asset's source format |
format |
String |
withFormat |
Accepts any of these values: gif, jpg, jpeg, pjpg, pjpeg, png, png8, webp |
lossless |
Boolean |
withCompression |
If true use lossless compression; if false use lossy compression |
quality |
Int |
withQuality |
Specify a value in the range of 0 to 100 |
dpr |
Int |
withDpr |
Specify a value in the range of 1 to 5 |
There are currently no arguments that are equivalent to the following functions of the
ImageUrlBuilder:
withCustomParamwithRectangleCropwithFocalPointCropwithFitMode
You can use it in a GraphQL query like the below:
query Assets {
assets: allAsset {
edges {
node {
id,
name,
url(width: 1200, format: "webp"),
placeholderUrl: url(width: 50, format: "webp"), # You can use aliases and supply different arguments to fetch multiple transformed URLs per query
type,
size,
description,
width,
height
}
}
}
}The default behaviour of this plugin when translating content from Kentico Kontent to objects in the Gridsome GraphQL data store should hopefully be sufficient in the majority of cases - a goal of the plugin is to allow consumers to get up and running with as little configuration as possible. However, the plugin does provide an extension point should you find yourself in a position where you want to modify the default behaviour.
Extending the translation of
TaxonomyandAssetobjects is not currently supported as those types are essentially comprised only of "system" fields and cannot be extended in Kentico Kontent; compared to content types that also have "system" fields, but are designed to be extended in Kentico Kontent by adding content elements.
The majority of the work required to translate content from Kentico Kontent to the Gridsome GraphQL data store is performed via a custom ContentItem model that is automatically passed to the delivery client as a type resolver.
The default behaviour is to use the same content model for all Kentico Kontent content types. This content model is represented by the GridsomeContentItem class, which is a sub-class of ContentItem.
To modify the default behaviour you can create a new class that extends GridsomeContentItem and register it as the type resolver for one or more Kentico Kontent content types.
For demonstration purposes we will use an example where we want to manipulate content data as it is inserted into Gridsome's data store so that we don't have to keep applying some logic on the data each and every time we use it within our application.
This is our scenario:
- There is a Kentico Kontent content type called
Postthat has a codename ofpost - The content type has a content element called
Datewith a codename ofdatethat can be used to manually specify the date that the post was posted, with the intention being to fall back to the system last modified date if noDatevalue is specified - When this plugin runs it creates an object type in the Gridsome GraphQL schema named
Post, but the field that represents theDatecontent element is calleddate1because it collides with the systemdatefield
The goal is to:
- Set the system
datefield to the value of theDatecontent element, but only if a value has been set; otherwise leave thedatefield value as it is - Remove the
date1field as it is redundant once thedatefield value has been resolved as above
To do this we must first create a custom content model somewhere in our application that extends GridsomeContentItem and implements the desired behaviour:
const { GridsomeContentItem } = require('@meeg/gridsome-source-kentico-kontent');
class PostContentItem extends GridsomeContentItem {
// Override the `addFields` function - this is called after all "system" fields are set
async addFields(node) {
/*
Call the `addFields` function of the base class - we want the default behaviour to run
first, and then we will manipulate the data to enforce our custom behaviour
*/
await super.addFields(node);
this.ensureDateField(node);
return node;
}
ensureDateField(node) {
/*
`node.item` contains the data that will eventually end up as an object in the
Gridsome GraphQL data store
*/
const postDate = new Date(node.item.date1);
const lastModified = new Date(node.item.date);
// If a date has been provided, use it instead of the system date
if (!this.isNullDate(postDate)) {
node.item.date = postDate;
}
// Add the system date as a custom field i.e. one that does not correspond to a content element
node.item.lastModified = lastModified;
// Remove the redundant `date1` field
node.item.date1 = undefined;
}
isNullDate(date) {
if (typeof(date) === 'undefined') {
return true;
}
if (date === null) {
return true;
}
// If a date value is null in Kentico Kontent it can be parsed as a zero UTC date
return date.getTime() === 0;
}
}
module.exports = PostContentItem;Now we have implemented our desired behaviour we need to register the class model as a type resolver.
Type resolvers can be registered via the options exposed by this plugin. To do so you must add an item to the contentItemConfig.contentItems object with a key matching the content type codename you wish to specify the type resolver for, and a value that references the content model class that you wish as the type resolver for that content type. For example:
const PostContentItem = require('./the/path/to/PostContentItem');
module.exports = {
...
plugins: [
{
use: '@meeg/gridsome-source-kentico-kontent',
options: {
...
contentItemConfig: {
...
contentItems: {
post: PostContentItem
}
...
}
...
}
}
}
...
}There could be any number of scenarios in which you may wish to extend GridsomeContentItem, but here are a few more examples of ways in which you can extend content models.
The addFields function seen earlier effectively loops through each content element defined on the field type and:
- Attempts to find and execute an instance function named
${fieldName}FieldResolverwherefieldNameis the codename of the content element converted to camel case; and if none is found - Attempts to find and execute an instance function named
${fieldType}TypeFieldResolverwherefieldTypeis the type of the content element converted to camel case; and if none is found - Executes an instance function named
defaultFieldResolver
The function that is executed receives a node and field as arguments:
noderepresents the data that will eventually be inserted into the Gridsome GraphQL data storefieldrepresents the content element data that has come from Kentico Kontent
The responsibility of the function is to set a value on the node that will contain the data from the field transformed into whatever format is deemed suitable for the Gridsome GraphQL data store.
Field resolvers can be used like this:
const { GridsomeContentItem } = require('@meeg/gridsome-source-kentico-kontent');
class PostContentItem extends GridsomeContentItem {
// This function will be used when resolving the "author" content element of this type
authorFieldResolver(node, field) {
const fieldName = field.fieldName;
const value = field.value;
// This is a pretty contrived example, but hopefully illustrates the point!
if (!value) {
value = 'Joe Bloggs';
}
this.addField(node, fieldName, value);
}
// This function will be used when resolving all "Multiple choice" content elements of this type
multipleChoiceTypeFieldResolver(node, field) {
const fieldName = field.fieldName;
// By default this value would be an object containing `name` and `codename` properties, but we just want `name`
const value = field.value.name;
this.addField(node, fieldName, value);
}
}
module.exports = PostContentItem;We saw earlier that you can extend how field data is set within a content model by overriding the addFields function. The addFields function receives a node object that represents the data from a single content item from Kentico Kontent transformed into a data structure that will be used to populate the Gridsome GraphQL data store.
In the previous example we used the item property of the node to get and set field values, but the node object has other properties that can be used when manipulating field data.
For example, in this scenario:
- We have a
Post seriescontent type in Kentico Kontent with a codename ofpost_series - The
Post seriescontent type has a "Linked items" content element calledPosts in serieswith codenameposts_in_series Posts in serieshas a constraint requiring at least one linked item be set
The goal is to:
- Add a
lastUpdatedfield to the correspondingPostSeriesobject type in the Gridsome GraphQL schema (there is no corresponding content element on the Kentico Kontent content type) - Set the value of the
lastUpdatedfield by getting the linked posts in itspostsInSeriesfield, and using the most recentdatevalue of the linkedPostobjects
const { GridsomeContentItem } = require('@meeg/gridsome-source-kentico-kontent');
class PostSeriesContentItem extends GridsomeContentItem {
async addFields(node) {
await super.addFields(node);
await this.setLastUpdatedField(node);
return node;
}
async setLastUpdatedField(node) {
// Find the `postsInSeries` field and then run it through a map function to return the most recent post date
const postsInSeriesFields = node.linkedItemFields
.filter(field => field.fieldName === 'postsInSeries');
const postLastUpdated = await Promise.all(
postsInSeriesFields.map(async field => this.getPostLastUpdated(field.linkedItems))
);
// Add the most recent post date as a new field called `lastUpdated`
const value = postLastUpdated[0];
// Calling the `addField` function will make sure there are no field name collisions
this.addField(node, 'lastUpdated', value);
}
async getPostLastUpdated(posts) {
const postDates = await Promise.all(
posts.map(async post => {
const node = await post.createNode();
const date = node.item.date;
return date;
})
);
const sortedPostDates = postDates.reduce((prevDate, currentDate) => {
return prevDate > currentDate ? prevDate : currentDate
});
return sortedPostDates;
}
}
module.exports = PostSeriesContentItem;When you are inside an instance function such as
addFieldsyou also have access to all of the properties of a regularContentItemsuch asthis.systemandthis._raw.elementsshould you need them.
GridsomeContentItem uses custom richTextResolver and urlSlugResolver functions to aid in the approach for rendering Rich Text fields, but if you decide to opt out of that approach you may want to provide your own resolver functions.
You can do so like this:
const { GridsomeContentItem } = require('@meeg/gridsome-source-kentico-kontent');
class PostContentItem extends GridsomeContentItem {
// We need to override the constructor of `GridsomeContentItem`
constructor(typeName, richTextHtmlTransformer) {
/*
Set our resolvers - N.B this `data` object mimics the object that you can pass to the `ContentItem` constructor
*/
const data = {
richTextResolver: (item, context) => {
return `<h3 class="resolved-item">${item.name.text}</h3>`;
},
urlSlugResolver: (link, context) => {
return { url: `/posts/${url_slug}` };
}
};
// Execute the super constructor, passing in your resolver functions
super(typeName, richTextHtmlTransformer, data);
}
}
module.exports = PostContentItem;The plugin uses the debug library for logging. It can be used for debugging or just gathering logs about what the plugin is doing.
The plugin defines the following namespaces:
| Namespace | Description |
|---|---|
gridsome-source-kentico-kontent |
Use this to log basic info about when the plugin starts and finishes its work |
gridsome-source-kentico-kontent:source |
Use this to log information about the work that the plugin is doing |
gridsome-source-kentico-kontent:delivery-client |
Use this to log information from the Kentico Kontent delivery client |
To have the log output to your console you will need to set a DEBUG environment variable to include one or more of the above namespaces, for example (the actual command to set the environment variable may differ depending on the OS or shell in use):
DEBUG=gridsome-source-kentico-cloud,gridsome-source-kentico-cloud:*
Please read the debug docs for full usage instructions.
The plugin exposes various configuration options that are split into the following top level objects:
plugins: [
{
use: '@meeg/gridsome-source-kentico-kontent',
options: {
deliveryClientConfig: {
// Options for the Kentico Kontent delivery client
},
contentItemConfig: {
// Options used when loading Kentico Kontent content data
},
taxonomyConfig: {
// Options used when loading Kentico Kontent taxonomy data
}
}
}
]The only required option that must be set for this plugin to function is
deliveryClientConfig.projectIdas seen in the getting started section. All other options are set with default values that can be overridden by the consuming application.
These options are identical to the Kentico Kontent delivery client configuration options, with one exception:
| Key | Type | Default value | Notes |
|---|---|---|---|
contentItemsDepth |
Number |
3 |
Sets the depth parameter on content queries, which can be used to handle missing referenced linked items |
Please see the Kentico Kontent documentation for a description of all other available options, which include options for setting preview mode, secure mode, and language.
| Key | Type | Default value | Notes |
|---|---|---|---|
contentItemTypeNamePrefix |
String |
'' |
If set, this value will be added as a prefix to generated content object type names |
assetTypeName |
String |
'Asset' |
If set, this value will be used as the asset object type name |
itemLinkTypeName |
String |
'ItemLink' |
If set, this value will be used as the item link object type name |
contentItems |
Object |
{} |
Please see the creating content models section |
richText |
Object |
Please see the richText options section |
| Key | Type | Default value | Notes |
|---|---|---|---|
wrapperCssClass |
String |
'rich-text' |
When used as a Vue template, Rich Text HTML must have a single root node and so the plugin wraps the HTML in a div with a class attribute set to this value; can be set to null to opt out of the default approach to rendering Rich Text fields |
componentNamePrefix |
String |
'' |
If set, this value will be added as a prefix to the component name used to render content components/items in Rich Text fields |
componentSelector |
String |
'p[data-type="item"]' |
This CSS selector is used to find elements that represent content components/items in Rich Text HTML; can be set to null to opt out of the default approach to rendering content components/items in Rich Text fields |
itemLinkComponentName |
String |
'item-link' |
This value will be used as the component name used to render content links in Rich Text fields |
itemLinkSelector |
String |
'a[data-item-id]' |
This CSS selector is used to find elements that represent content links in Rich Text HTML; can be set to null to opt out of the default approach to rendering content links in Rich Text fields |
assetComponentName |
String |
'asset' |
This value will be used as the component name used to render assets in Rich Text fields |
assetSelector |
String |
'figure[data-asset-id]' |
This CSS selector is used to find elements that represent assets in Rich Text HTML; can be set to null to opt out of the default approach to rendering assets in Rich Text fields |
| Key | Type | Default value | Notes |
|---|---|---|---|
taxonomyTypeNamePrefix |
String |
'Taxonomy' |
If set, this value will be added as a prefix to generated taxonomy object type names |