Skip to main content
Version: 2.x

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).

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โ€‹

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:

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);
tip

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 Title
  • image of type Image
  • text 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:

my-module/schema.gql
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:

my-module/resolvers.js
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:

my-module/index.js
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:

my-module/schema.gql
type Homepage {
title: String
image: String
+ thumbnail: String
text: DefaultWysiwyg
}

extend type Query {
homepage: Homepage
}

And in the resolver:

my-module/resolver.js
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:

my-module/schema.gql
type Homepage {
title: String
image: String
+ imageAlt: String
thumbnail: String
+ thumbnailAlt: String
text: DefaultWysiwyg
}

extend type Query {
homepage: Homepage
}

In the resolver:

my-module/resolver.js
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:

my-module/resolver.js
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:

my-module/schema.gql
type Homepage {
- title: String
+ title: DefaultWysiwyg
image: String
text: DefaultWysiwyg
}

extend type Query {
homepage: Homepage
}

And in the module:

my-module/index.js
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 Text
  • content of type Rich Text

Implement the schemaโ€‹

We can model the corresponding GraphQL schema as follows:

my-article-module/schema.gql
type Article {
uid: String
title: DefaultWysiwyg
content: DefaultWysiwyg
}

extend type Query {
article(slug: String!): Article
}
tip

The slug argument represents the value of the uid field.

Implement the resolverโ€‹

my-article-module/resolver.js
export default {
Query: {
article: (_, { slug }, { loaders }) => {
return loaders.Prismic.loadByUID("article", slug);
},
},
};

Define the content transform optionsโ€‹

my-article-module/index.js
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โ€‹

Reminder

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 :

my-article-module/resolver.js
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 Text
  • answer of type Rich Text
  • link 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:

my-faq-module/schema.gql
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:

my-faq-module/resolver.js
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โ€‹

my-faq-module/index.js
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 {};
},
};

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:

my-faq-module/index.js
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 {}
}
}