Expose Prismic Content
The Prismic Front-Commerce module provides a loader and the infrastructure to expose Prismic based Content in Front-Commerce’s GraphQL API.
Prerequisites
To benefit from this API, you first need to install the Prismic module. You will also need a custom GraphQL module to define a GraphQL schema matching the content you want to expose and implement the corresponding resolver. It also recommended to have read the Prismic Core Concepts documentation.
Prismic loader API
Methods to query content
The Prismic loader has the following API to request Content from Prismic:
loadSingle(typeIdentifier[, options])
Returns an object of type Content
or throws an error if no such Content exists.
Arguments:
- typeIdentifier (string): The type of document.
- options (ContentTransformOptions): Content transform options
loadByID(id[, options])
Returns an object of type Content
or throws an error if no such Content exists.
Arguments:
id (string)
: The ID of the document .- options (ContentTransformOptions): Content transform options
loadByUID(typeIdentifier, uid[, options])
Returns a Content
representing a Prismic Content of the corresponding type and having an UID field with the given value. If such Content does not exist, it throws an error.
Arguments:
typeIdentifier (string)
: The type of document.- uid (string): The Unique ID of the document.
- options (ContentTransformOptions): Content transform options
loadList(query[, options])
Returns a ContentList
matching the query. query
must be an instance of ListQuery
, it provides a way to filter, sort and paginate Prismic Content.
Arguments:
- query (ListQuery): A query for a list of documents
- options (ContentTransformOptions): Content transform options
Field Transformers
By default, the Content
objects returned by those methods directly expose the fields defined in the corresponding Prismic Custom Type. For instance if you are retrieving a Content of type homepage
and this Custom Type is single and defines a lastUpdate
field, you can write something like:
const homepage = await loaders.Prismic.loadSingle("homepage");
// homepage is an instance of Content
// the lastUpdate field can be used directly,
// it's value is the field value returned by the Prismic API
console.log("Last update of my homepage", homepage.lastUpdate);
For most Field Types, the field value can directly be used. However for some of them, the field value is not very handy to expose the data in the Graph or even requires a deep transformation. To make that operation easier, the Prismic loader also exposes some Field Transformers to transform field values. Instances of the transformers can be passed to loadSingle
, loadByID
, loadByUID
and loadList
to transform the loaded Prismic Content. In the previous example, if the lastUpdate
field is a Date Field, homepage.lastUpdate
will be a string. If instead you want to have a JavaScript Date
instance as the field value, you can configure loadSingle
to transform it:
const { DateTransformer } = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
lastUpdate: new DateTransformer(),
},
});
// homepage.lastUpdate is now a Date object
console.log("Last update of my homepage", homepage.lastUpdate);
The complete list of available Transformers can be found under the transformers
directory in Prismic module. We will use some of them in the following examples.
Implementation examples
Those examples assume that you have created a GraphQL module and that it is already registered. Before being able to use the Prismic module API, you have to make sure Prismic/Core
is a dependency of your module. In addition, we will use the Wysiwyg feature provided by Front-Commerce/Wysiwyg
, so this one also needs to be in the dependencies:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "MyModule",
- dependencies: [],
+ dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
}
Expose a Homepage in the Graph
Define the schema
Let’s assume you have created a Single Custom Type Homepage whose identifier is homepage
. This Custom Type has three fields:
title
of type Titleimage
of type Imagetext
of type Rich Text
While not mandatory, the best way to expose the Homepage in the Graph is to model the GraphQL type after the Custom Type. So in schema.gql
, you can add:
type Homepage {
title: String
image: String
text: DefaultWysiwyg
}
extend type Query {
homepage: Homepage
}
Implement the resolver
Once you have defined the type in the Graph, you need to implement the corresponding resolver in resolvers.js
to retrieve the homepage Content from Prismic:
export default {
Query: {
homepage: async (root, args, { loaders }) => {
const {
TitleTransformer,
RichtextToWysiwygTransformer,
ImageTransformer,
} = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
title: new TitleTransformer(),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return homepage;
},
},
};
Image field handling
In this example, without a dedicated resolver, the image
field of the Homepage
type is the path to the image of the main
view. In Prismic, while configuring an Image field in the Custom Type editor, it is possible to define several views to get the same image under a different format. Those views can also be exposed in the Graph. For instance, if you have defined a thumbnail
view, it can be exposed in the graph by applying the following changes. In schema.gql
:
type Homepage {
title: String
image: String
+ thumbnail: String
text: DefaultWysiwyg
}
extend type Query {
homepage: Homepage
}
And in the resolver:
export default {
Query: {
homepage: async (root, args, { loaders }) => {
const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
title: new TitleTransformer(),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return homepage;
},
},
+ Homepage: {
+ thumbnail: (homepageContent) => {
+ return homepageContent.image.thumbnail;
+ }
+ }
};
The
ImageTransformer
also rewrites the image path provided by the Prismic API to use a custom image proxy defined by the module. This path rewrite makes those image path directly usable by Front-Commerce’s<Image />
component.
In addition, each view carries some metadata like the alternative text or the image dimensions. The following changes allow to expose the alternative text of both the thumbnail
and the main
views:
type Homepage {
title: String
image: String
+ imageAlt: String
thumbnail: String
+ thumbnailAlt: String
text: DefaultWysiwyg
}
extend type Query {
homepage: Homepage
}
In the resolver:
export default {
Query: {
homepage: async (root, args, { loaders }) => {
const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
title: new TitleTransformer(),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return homepage;
},
},
Homepage: {
+ imageAlt: (homepageContent) => {
+ return homepageContent.image.main.alt;
+ },
thumbnail: (homepageContent) => {
return homepageContent.image.thumbnail;
},
+ thumbnailAlt: (homepageContent) => {
+ return homepageContent.image.thumbnail.alt;
+ },
}
};
Title field handling
In this example, the title is exposed as a plain string in the graph. However, the TitleTransformer
also generates an HTML representation of the field. So if you need to retrieve the HTML code instead, you can define a custom resolver on the title
field of the Homepage
type:
export default {
Query: {
homepage: async (root, args, { loaders }) => {
const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
title: new TitleTransformer(),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return homepage;
},
},
+ Homepage: {
+ title: (homepageContent) => homepageContent.title.html;
+ }
};
In addition, Title and Rich Text fields are very similar; a Title field can be seen as a restricted Rich Text field. As a result, the Transformers dedicated to Rich Text fields can also be used on Title fields. So if you want to expose the title as a DefaultWysiwyg
, you can do the following changes instead. In the schema.gql
:
type Homepage {
- title: String
+ title: DefaultWysiwyg
image: String
text: DefaultWysiwyg
}
extend type Query {
homepage: Homepage
}
And in the resolver:
export default {
Query: {
homepage: async (root, args, { loaders }) => {
- const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
+ const { RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
const homepage = await loaders.Prismic.loadSingle("homepage", {
fieldTransformers: {
- title: new TitleTransformer(),
+ title: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return homepage;
},
},
};
Expose an article and retrieve it by its uid
Let’s assume you have created a Custom Repeatable Type Article which identifier is article
. This Custom Repeatable Type has three fields:
uid
of type UID (this field is mandatory to retrieve the article by its identifier)title
of type Rich Textcontent
of type Rich Text
Implement the schema
We can model the corresponding GraphQL schema as follows:
type Article {
uid: String
title: DefaultWysiwyg
content: DefaultWysiwyg
}
extend type Query {
article(slug: String!): Article
}
Note The slug argument represent the value of the uid
field.
Implement the resolver
export default {
Query: {
article: (_, { slug }, { loaders }) => {
const { RichtextToWysiwygTransformer } = loaders.Prismic.transformers;
return loaders.Prismic.loadByUID("article", slug, {
fieldTransformers: {
// no transformer needed for `uid` field
title: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
content: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
},
},
};
loadByUID
error handling
Reminder loadByUID returns a Content
representing a Prismic Content of the corresponding type and having an UID field with the given value. If such Content does not exist, it throws an error.
We can handle the error by wrapping our promise in a try / catch :
export default {
Query: {
article: async (_, { slug }, { loaders }) => {
const { RichtextToWysiwygTransformer } = loaders.Prismic.transformers;
try {
return await loaders.Prismic.loadByUID("article", slug, {
fieldTransformers: {
// no transformer needed for `uid` field
title: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
content: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
} catch (e) {
// return whatever default value
return null;
}
},
},
};
Expose a list of FAQs
Define the schema
Let’s assume you have created a Custom Type FAQ which identifier is faq
. This Custom Type has three fields:
question
of type Key Textanswer
of type Rich Textlink
of type Link
Like in the previous example, we can model the corresponding GraphQL type after the Custom Type and in this case we add a root query to retrieve a list of FAQ with a basic pagination and search capabilities:
type Faq {
question: String
answer: DefaultWysiwyg
link: String
}
input FaqQueryInput {
page: Int
search: String
}
extend type Query {
faqList(params: FaqQueryInput): [Faq]
}
Implement the resolver
Again, we have to implement the corresponding resolver by using loadList
and ListQuery
described above:
const pageSize = 10;
export default {
Query: {
faqList: async (root, { params }, { loaders }) => {
const { search, page } = params;
const { LinkTransformer, RichtextToWysiwygTransformer } =
loaders.Prismic.transformers;
const ListQuery = loaders.Prismic.queries.ListQuery;
const query = new ListQuery(pageSize, page ? page : 1);
query.type("faq").sortBy("document.last_publication_date", "asc");
if (search) {
query.search(search);
}
const faqList = await loaders.Prismic.loadList(query, {
fieldTransformers: {
// no transformer needed for `question` field as it's a key text field
answer: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
link: new LinkTransformer(),
},
});
return faqList.list;
},
},
};
Handling links
Both the LinkTransformer
and RichtextToWysiwygTransformer
can be configured to recognize local URLs and rewrite them as site relative links. That way, those links won’t break the SPA navigation and contributors don’t have to worry about the environment when adding a link.
To benefit from that feature, the following changes have to be applied to the resolver:
const pageSize = 10;
export default {
Query: {
faqList: (root, { params }, { loaders }) => {
const { search, page } = params;
const { LinkTransformer, RichtextToWysiwygTransformer } = loaders.Prismic.transformers;
const ListQuery = loaders.Prismic.queries.ListQuery;
const query = new ListQuery(pageSize, page ? page : 1);
query.type("faq").sortBy("document.last_publication_date", "asc");
if (search) {
query.search(search);
}
+ const linkTransformer = new LinkTransformer([
+ "localhost",
+ "staging.example.com",
+ "production.example.com"
+ ]);
const faqList = await loaders.Prismic.loadList(query, {
fieldTransformers: {
// no transformer needed for `question` field as it's a key text field
- answer: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
- link: new LinkTransformer(),
+ answer: new RichtextToWysiwygTransformer(loaders.Wysiwyg, linkTransformer),
+ link: linkTransformer,
},
});
return faqList.list;
},
},
};