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. This guide explains how to use it.
Exposing Prismic content in an existing Front-Commerce application GraphQL schema is one of the main use case Prismic projects. We tried to keep the API easy to understand and require as little code as possible (while keeping it maintainable and expressive).
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โ
The Prismic loader has the following API to request Content from Prismic:
loadSingle(typeIdentifier)
โ
Returns a Content representing a Prismic Content of the corresponding type. If such Content does not exist, it throws an error.
Arguments:
typeIdentifier
(string): The type of document.
loadByID(id)
โ
Returns a Content representing a Prismic Content of the corresponding type. If such Content does not exist, it throws an error.
Arguments:
id
(string): The ID of the document .
loadByUID(typeIdentifier, uid)
โ
Returns a Content representing a Prismic Content of the corresponding type and having a 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.
loadList(query)
โ
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
defineContentTransformers(typeIdentifier, options)
โ
Defines the content transform options for a given document type.
Arguments:
typeIdentifier
(string): The type of document.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. The field
transformers can be registered for a given content type by using the
defineContentTransformers
method. 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, define the transformers for homepage
:
const { DateTransformer } = loaders.Prismic.transformers;
// this will apply to any method loading the `homepage` content type
loaders.defineContentTransformers("homepage", {
fieldTransformers: {
lastUpdate: new DateTransformer(),
},
});
const homepage = await loaders.Prismic.loadSingle("homepage");
// homepage.lastUpdate is now a Date object
console.log("Last update of my homepage", homepage.lastUpdate);
defineContentTransformers
only needs to be defined once per content type, and
it's usually better to defined at a higher level in your module, for example in
the
contextEnhancer
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: "my-module",
- 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: (root, args, { loaders }) => {
return loaders.Prismic.loadSingle("homepage");
},
},
};
Define the content transform optionsโ
Now we want to ensure that the fields are correctly transformed for the
homepage
content type. To do that, we need to define the content transform
options for homepage
:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "my-module",
dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
contextEnhancer: ({ loaders }) => {
const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } =
loaders.Prismic.transformers;
loaders.defineContentTransformers("homepage", {
fieldTransformers: {
title: new TitleTransformer(),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return {};
},
};
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: (root, args, { loaders }) => {
return loaders.Prismic.loadSingle("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: (root, args, { loaders }) => {
return loaders.Prismic.loadSingle("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: (root, args, { loaders }) => {
return loaders.Prismic.loadSingle("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 module:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "my-module",
dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
contextEnhancer: ({ loaders }) => {
- const { TitleTransformer, RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
+ const { RichtextToWysiwygTransformer, ImageTransformer } = loaders.Prismic.transformers;
loaders.defineContentTransformers("homepage", {
fieldTransformers: {
- title: new TitleTransformer(),
+ title: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
image: new ImageTransformer(),
text: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
})
return {}
}
}
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
}
The slug argument represents the value of the uid
field.
Implement the resolverโ
export default {
Query: {
article: (_, { slug }, { loaders }) => {
return loaders.Prismic.loadByUID("article", slug);
},
},
};
Define the content transform optionsโ
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "my-article-module",
dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
contextEnhancer: ({ loaders }) => {
const { RichtextToWysiwygTransformer } = loaders.Prismic.transformers;
loaders.defineContentTransformers("article", {
fieldTransformers: {
// no transformer needed for `uid` field
title: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
content: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
},
});
return {};
},
};
loadByUID
error handlingโ
loadByUID returns a Content
representing a Prismic Content of the
corresponding type and having a 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 }) => {
try {
return await loaders.Prismic.loadByUID("article", slug);
} 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 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);
return faqList.list;
},
},
};
Define the content transform optionsโ
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "my-faq-module",
dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
contextEnhancer: ({ loaders }) => {
const { LinkTransformer, RichtextToWysiwygTransformer } =
loaders.Prismic.transformers;
loaders.defineContentTransformers("faq", {
fieldTransformers: {
// no transformer needed for `question` field as it's a key text field
answer: new RichtextToWysiwygTransformer(loaders.Wysiwyg),
link: new LinkTransformer(),
},
});
return {};
},
};
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 content transformers:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "my-faq-module",
dependencies: ["Prismic/Core", "Front-Commerce/Wysiwyg"],
typeDefs,
resolvers,
contextEnhancer: ({ loaders }) => {
const { LinkTransformer, RichtextToWysiwygTransformer } =
loaders.Prismic.transformers;
+ const linkTransformer = new LinkTransformer([
+ "localhost",
+ "staging.example.com",
+ "production.example.com"
+ ]);
loaders.defineContentTransformers("faq", {
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 {}
}
}