Prismic Routable Types
You might want to create new pages managed from Prismic. These pages should be accessible with a URL and may have to be included in the Sitemap with a clean URL. This guide explains everything that is supported by Front-Commerce's Prismic module.
The Prismic module allows to leverage Front-Commerce's Dispatcher to make content from custom types routable.
Create a field to represent the Url of the custom type
To create a custom field to represent the Url of the custom type:
- Head over to the Prismic console and navigate to custom types
(
https://<prismic_repository_name>.prismic.io/masks/
) - Select the custom type to make routable
- Drag and drop
Key Text - Text field for exact match search
from the right to the Main tab on the left. - Give the new field a
Field name
,API ID
and optionally aField placeholder
(note the API ID is an important field and will be used below) - Click ok to confirm the inputs
- Save the changes
For the sake of simplicity we will assume the name of this field is url
for
the rest of this documentation
Create a GraphQL type to represent the custom type
If no GraphQL type was created to represent the custom data type within Front-Commerce, please create a GraphQL type to represent the custom type in schema.gql of the server module's directory
type Album implements Routable {
url: String # the newly added field
artist: String
title: String
release_date: Date
path: String # for the routable interface
}
Note that in order for the custom type to be routable it needs to implement the
core Front-Commerce
Routable interface.
That is why the GraphQL type must have a field called path
to represent the
URL of the routable type.
Add a new GraphQL type to the module's resolver:
export default {
Query: {
...
},
+ Album: {
+ path: async (content, _, { loaders }) => {
+ const baseUrl = (await loaders.Shop.getCurrentShop()).baseUrl,
+ return `{baseUrl}/${content.url}`;
+ },
+ },
+};
Note we added a path field resolver that appends the shop's baseUrl to the url
field of our custom Prismic type.
Add GraphQL type to the dispatcher query
Now you need to add your type to the GraphQL dispatcher query. For more info on the topic please refer to Add GraphQL type to the dispatcher query section of the route dispatcher documentation
Register the Prismic custom type as a routable type
To make a custom Prismic type routable it must be registered with the Prismic
module. First ensure that the Prismic/Core
is added as a module's dependency
then use loaders.Prismic.registerRoutableType
to add the custom type as a
routable type:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "<module_name_space>",
- dependencies: [],
+ dependencies: ["Prismic/Core"],
typeDefs,
resolvers,
contextEnhancer: ({ req, loaders }) => {
+ const { TitleTransformer, DateTransformer } = loaders.Prismic.transformers;
+ loaders.Prismic.defineContentTransformers("album", {
+ fieldTransformers: {
+ title: new TitleTransformer(),
+ release_date: new DateTransformer(),
+ },
+ })
+ loaders.Prismic.registerRoutableType({
+ typeIdentifier: "album", // <-- Prismic custom type
+ urlFieldName: "url", // <-- Prismic API ID mentioned above
+ graphQLType: "Album", // <-- GraphQL type created above
+ path: "/albums/:url", // <-- The dynamic route. Examples: '/:uid', '/:lang/:uid', '/:section/:category?/:uid'.
+ isSitemapable: false,
+ postTransformer: (url, document, params) => { // optional function postTransformer
+ if(document.isPublished) { // possible use case.
+ return document;
+ }
+ }
+ });
return {};
},
};
The postTransformer
above is optional. It is a function that will be called
after
the transformation is done using contentTransformOptions
.
It is given the current URL being resolved and the transformed document. It can
be used if you have some custom logic to apply to the document or if you want to
prevent the document from showing using some custom logic (returning a falsy
value).
If you have a nested route e.g. /albums/:category/:uid
then you need add the
resolvers
that list the Content Relationships identifiers in the route, in
this example the album
has a content relationship to category
.
loaders.Prismic.registerRoutableType({
typeIdentifier: "album",
- path: "/albums/:url",
+ path: "/:category/:url",
+ resolvers : {
+ category: "category",
+ }
});
The Route Resolver is limited to retrieving data from 2 levels deep, please see the Route Resolver example for more information.
Map GraphQL type to a component
To map a GraphQL type to React component, create a file called moduleRoutes.js
at the root of the web
directory and add the following to it:
import React from "react";
export const dispatchedRoutes = {
Album: ({ loading, matched }) => {
if (loading) {
return <div>Loading...</div>;
}
if (!matched) {
return <div>No Album</div>;
}
const { title, artist, release_date } = matched;
return (
<div>
<h3>Album:</h3>
{title}
<br />
{artist}
<br />
{release_date?.toString()}
<br />
</div>
);
},
};
For more details please refer to Add the mapping between the type and the page component in the route dispatcher.
Now an instance of the custom Prismic type with the url
field set to
my-custom-url
will be accessible at https://<website_domain>/my-custom-url
(or https://<website_domain>/<shop_name>/my-custom-url
if in a multi shop
environment)
Add a routable custom type to the sitemap
Optionally to add the custom Prismic type to the sitemap use the following steps:
- implement the Sitemapable interface on the GraphQL type, and resolver.
-type Album implements Routable {
+type Album implements Routable & Sitemapable {
url: String # the newly added field
artist: String
title: String
release_date: Date
path: String # for the routable interface
+ priority: Float
+ seoImages: [SitemapImage]
+ lastmod: String
+ changefreq: String
}
and
export default {
Query: {
...
},
Album: {
path: async (content, _, { loaders }) => {
const baseUrl = (await loaders.Shop.getCurrentShop()).baseUrl,
return `{baseUrl}/${content.url}`;
},
+ priority: () => 1,
+ seoImages: (content) => [],
+ lastmod: () => "2020-01-01T12:00:00.000Z",
+ changefreq: () => "daily",
},
};
- Enable sitemap in the module definition file:
import typeDefs from "./schema.gql";
import resolvers from "./resolvers";
export default {
namespace: "<module_name_space>",
dependencies: ["Prismic/Core"],
typeDefs,
resolvers,
contextEnhancer: ({ req, loaders }) => {
const { TitleTransformer, DateTransformer } = loaders.Prismic.transformers;
loaders.Prismic.defineContentTransformers("album", {
fieldTransformers: {
title: new TitleTransformer(),
release_date: new DateTransformer(),
},
})
loaders.Prismic.registerRoutableType({
typeIdentifier: "album", // <-- the Prismic custom type
urlFieldName: "url", // <-- Prismic API ID mentioned above
graphQLType: "Album", // <-- GraphQL type created above
- isSitemapable: false,
+ isSitemapable: true,
postTransformer: (url, document) => { // optional function postTransformer
if(document.isPublished) { // possible usecase
return document;
}
}
});
return {};
},
};
Methods
registerRoutableType(options)
Registers a routable type within Front-Commerce and adds the route to the Prismic Route Resolver, it can also create a sitemapable route.
Options:
typeIdentifier
(string): the Prismic custom type identifierurlFieldName
(string): the Prismic field name that contains the URLgraphQLType
(string): the GraphQL type to map to the custom typepath
(string): the dynamic route. Examples:/:uid
,/:lang/:uid
,/:section/:category?/:uid
.rewrites
(string[]) : the rewrite paths to apply on a url, they will be redirected the base path.isSitemapable
(boolean): whether the type should be included in the sitemappostTransformer
(PostTransformerCallback): a function that will be called after the transformation is done usingcontentTransformOptions
Example:
loaders.Prismic.registerRoutableType({
typeIdentifier: "foo",
graphQLType: "Foo",
urlFieldName: "uid",
path: "/foo/bar/:uid",
rewrites: ["/baz/bar/:uid"],
resolvers: {
category: "category",
},
isSitemapable: true,
postTransformer: (url, document, params) => {
if (document.isPublished) {
return document;
}
},
});
registerPrismicRoute(options)
To leverage the
Prismic Route Resolver,
you add a route to the Route Resolver using the registerPrismicRoute
method.
Options:
typeIdentifier
(string): the Prismic custom type identifierpath
(string): the dynamic route. Examples:/:uid
,/:lang/:uid
,/:section/:category?/:uid
.
Example:
You might have a document type foo
with a field bar
and baz
, but only
foo
is a routable type.
This method will allow you to add a route to the Route Resolver and the Prismic
client which will in turn be able to resolve a url
for the document type. e.g.
loaders.Prismic.registerRoutableType({ // This method registers a routable type
type: "foo",
path: "/foo-path/:uid",
...
});
loaders.Prismic.registerPrismicRoute({ // This method registers a route to the Route Resolver
type: "bar",
path: "/foo-path/:url" // this is a property in bar
});
loaders.Prismic.registerPrismicRoute({
type: "baz",
path: "/foo-path/:url" // this is a property in baz
});
It's also important to note that only one route for a document type can be added to the route resolver, the last registered route will override the previous route e.g.
loaders.Prismic.registerPrismicRoute({
type: "foo",
path: "/foo/:uid",
});
loaders.Prismic.registerPrismicRoute({
type: "foo",
path: "/bar/:uid",
});
// output for foo type is /bar/:uid
This method will not create a resolvable route in Front-Commerce. If you need a routable type please use the
registerRoutableType
method instead, it also registers a prismic route.
Advanced usage
Trailing Slash
By default Front-Commerce will redirect urls with trailing slashes to their
counterpart without a trailing slash. For example /album/
will redirect to
/album
. You can configure this behavior to act the opposite way, where urls
without trailing slashes are redirected to their counterparts with trailing
slashes.
To achieve this you can add a trailing slash to the path
on the
registerRoutableType
method:
PrismicLoader.registerRoutableType({
- path: "/album/:uid",
+ path: "/album/:uid/", // this will enforce redirects to trailing slash
...
})
You can use the online express-route-tester@2.0.0 to test your paths.
path | url | result |
---|---|---|
/:uid | /foo | match |
/:uid | /foo/ | redirect |
/:uid/ | /foo | redirect |
/:uid/ | /foo/ | match |
Path Rewrites
Rewrites allow you to map an incoming request path to a different destination
path. Rewrites act as a URL proxy and mask the destination path, making it
appear the user hasn't changed their location on the site. In contrast,
redirects will reroute to a new page and show the URL changes. To use rewrites
you can use the rewrites
key in the registerRoutableType
method:
PrismicLoader.registerRoutableType({
typeIdentifier: "foo",
path: "/baz/:uid",
rewrites: ["/foo/:uid", "/bar/:uid"],
});
Content Relationship Resolvers
When using nested paths /:section/:category/:uid
you should use the
resolvers
to map the :category
and :section
relationships to the Prismic
document fields.
The nesting is currently limited at 2 levels deep.
// Level 1 (The document being queried via the uid)
{
type : "album"
title: "Gates of Thorns",
uid: "gates-of-thorns",
parent_category: "hard-rock", <- Relationship Field
}
// Level 2 (Relationship to album)
{
type: "category",
title: "Hard Rock",
uid: "hard-rock"
parent: "rock" <- Relationship Field
}
// level 3 (Relationship to category)
{
type: "category",
title: "Rock",
uid: "rock",
parent: "music" <- Relationship Field
}
As you may notice the relationships fields can continue infinitely deep, but as mentioned they are limited to the second level.
loaders.Prismic.registerRoutableType({
typeIdentifier: "album",
urlFieldName: "uid",
graphQLType: "Album",
path: "/:level1?/:level2?/:level3?/:uid",
resolvers: {
level1: "parent_category",
level2: "parent_category.parent",
level3: "parent_category.parent.parent", ❌ this will fail as it's trying to access the 3rd nested level
},
});
loaders.Prismic.registerRoutableType({
typeIdentifier: "album",
urlFieldName: "uid",
graphQLType: "Album",
path: "/:level1?/:level2?/:uid",
resolvers: {
level1: "parent_category",
level2: "parent_category.parent",
},
});
Here are some examples of how the paths would be resolved for this registered route.
// Level 1
{
uid: "gates-of-thorns",
parent_category: "hard-rock", <- Relationship Field
}
// Level 2
{
uid: "hard-rock"
parent: "rock" <- Relationship Field
}
path: `/:level1?/level2?/:uid`
url: `/rock/hard-rock/gates-of-thorns`
// Level 1
{
uid: "gates-of-thorns",
}
path: `/:level1?/level2?/:uid`
url: `/gates-of-thorns`