gatsby-source-prismic-graphql
gatsby-source-prismic-graphql copied to clipboard
Creating dynamic pages that have a parent relationship?
I have been reading through the docs on dynamic page generation and I have a good working solution. (Doc: https://github.com/birkir/gatsby-source-prismic-graphql#automatic-page-generation)
My current solution:
{
resolve: "gatsby-source-prismic-graphql",
options: {
repositoryName: "example-repo-name",
path: "/preview",
previews: true,
pages: [
{
type: "Page",
match: "/:uid",
path: "/page-preview",
component: require.resolve("./src/templates/page.js"),
},
],
},
}
This works fine and creates pages like so (at the top level):
/about /contact /sales
However, in Prismic, some of these pages have a relationship field where the author can select a "parent" page.
Is there any possible way to generate dynamic pages with a URL pattern like:
/parent-page/sales
...where the parent relationship has its own uid prefixed before the page uid?
The query within the page.js
template is as simple as:
query PageQuery($uid: String!) {
prismic {
allPages(uid: $uid) {
edges {
node {
title
}
}
}
}
}
I guess what I'd like to know is if this is even possible at all with the current plugin?
A nudge in the right direction on how to achieve a nested URL structure/scheme would be greatly appreciated.
Thank you!
I also would like to know if this is possible as it is a very common use case!
Hi @kamerondotcom and @michaelpumo,
Currently it's impossible to inject params in the url that are not part of the medata coming from the main document that you're linking to. I'm currently working on an implementation that will allow you to setup any kind of custom param in the URL relying on custom resolvers. Basically it means that you'll manage to inject data coming from a document link like a parent category but also from any other field in your document like a text field. I'm still thinking of the best way to implement it but it will probably be something closer to this:
pages: [{
type: 'Blog', // Custom type of the document
match: '/:lang/blog/:parentCategory?/:category?/:uid', // Pages will be generated in this pattern
path: '/blog', // Placeholder route for previews
queryParams: {
query: `
category {
... on PRISMIC_Category {
_meta {
uid
}
parent_category {
... on PRISMIC_Category {
_meta {
uid
}
}
}
}
}
`,
category: (metaNode) => metaNode.category._meta.uid,
parentCategory: (metaNode) => {
return metaNode.category.parent_category._meta.uid
}
}]
I still don't know about naming but basically the queryParams
object ask for a piece of query to fetch the right data and then below you have the resolvers that will match the custom param names in the url that will take a node as parameter and you'll just need to specify the path to the data you need to fill the URL.
Does it makes sense?
That sounds great @arnaudlewis. I have a solution below for now but it would be great to get an official method for this.
What I did if anyone is interested (@kamerondotcom).
gatsby-config.js
const { apiEndpoint } = require("./prismic-config")
const repo = /([^\/]+)\.prismic\.io\/graphql/.exec(apiEndpoint)
module.exports = {
{
resolve: "gatsby-source-prismic-graphql",
options: {
repositoryName: repo[1],
path: "/preview",
previews: true,
sharpKeys: [/image|photo|picture|logo/],
// Do not use. We build pages manually in gatsby-node.js
// pages: [
// {
// type: "Page",
// match: "/:uid",
// path: "/page-preview",
// component: require.resolve("./src/templates/page.js"),
// },
// ],
},
},
],
}
gatsby-node.js
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(`
query PageQuery {
prismic {
allPages {
edges {
node {
_meta {
uid
}
parent {
... on PRISMIC_Page {
_meta {
id
uid
}
}
}
}
}
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`🔥 Error while running GraphQL query on Prismic.`)
return
}
const pageTemplate = require.resolve(`./src/templates/page.js`)
console.log(`🙏🏼 Begin creating pages from Prismic...`)
result.data.prismic.allPages.edges.forEach(({ node }) => {
const parent = node.parent ? node.parent._meta.uid : ""
const slug = node._meta.uid
const url = parent ? `${parent}/${slug}` : `${slug}`
const parentId = parent ? node.parent._meta.id : ""
console.log(`✅ Page: ${url}`)
createPage({
path: url,
component: pageTemplate,
context: {
parentId: parentId,
uid: slug,
},
})
})
console.log(`👌🏼 Done creating pages from Prismic!`)
}
This works and builds pages with parents correctly. However, it's not recursive and only goes one level deep. You could go deeper if you wanted to but it will always be a fixed level unless I can think of a clever solution to it.
Also, I think this breaks the preview mechanism now. Not sure how to get around that?
Thanks
Yes but links between pages and preview rely on the link resolver and so for this, you need all the metadata from your doc links to be able to properly generate it. I’m my implementation, I’ll generate the link resolver from your gatsby config so you won’t even have to care about it ;)
Heya @arnaudlewis - you're absolutely right. I had to do some trickery to get links working too. It's limited to known links...if they add an adhoc link via richtext then I guess it would break. Darn! Need to error-proof this for the meantime.
Hi @arnaudlewis, we're desperate for nested path support but don't want to go down the gastsby-node.js
route as we rely very heavily on previews. Do you have a view on timescales for nested paths support? Many thanks.
PS Awesome project btw :-)
Hi @arnaudlewis, we need this same capability. Is this on the roadmap?
Hi guys, actually, the feature is developed and ready for any Prismic user and we'll be adapted to Gatsby so It needs some tests on our end but I can work on a small sample to showcase how it will work.
Basically, it will look almost like I described before but the resolvers
will be more declarative than functions and as soon as you have this kind of routing model defined, It will automatically add a url
field on each document queried from the writing room.
It means that the link resolver won't be necessary anymore to resolve a URL on your website ;)
Hi @arnaudlewis. Do you have an ETA when this will be implemented for Gatsby? Do you have that small sample to showcase how it will work? Thanks!
@arnaudlewis our use case is even a bit simpler. We has a slug_prefix
field on all our pages that allows content editors to put whatever they want (ie /products/) then it appends the uid to the end. As @Jrousell said we ended up having to do this with a custom page resolver in gatsby-node.js
but this has broken previews:
pages.edges.forEach(entry => {
const { node: { _meta: {uid}, slug_prefix } } = entry
let prefix = slug_prefix || ''
// lets trim the start and end / just to make things consistent
prefix = prefix.replace(/^\/+|\/+$/g, '') + '/'
// If we come across a page with the slug 'home' we'll make this the homepage
const slug = uid === 'home' ? '/' : prefix + uid
createPage({
context: {
uid,
},
path: slug,
component: path.resolve("src/templates/page.js"),
})
})
Is there a way we can just write a custom resolver. You mentioned something was implemented. Is there any documentation you can point us to or provide an example here?
@arnaudlewis Any news on this one? Is this feature still under development by the Prismic team?