I'm building a custom plugin that works with a CMS that supports relational data (specifically, one-to-one and one-to-many relationships).
Is there a good way to implement these relations within the generated schema such that they feel as if they were created with gatsby-config's mapping option, but entirely within the plugin itself?
More concretely, suppose our plugin's user has two collections in their CMS, BlogPost and Author:
{
type: 'BlogPost',
id: 'blog-post-42',
// Each `BlogPost` can have 0 or more `Authors`
authors: ['author-123', 'author-456'],
...
}
{
type: 'Author',
id: 'author-123',
...
}
If they were to _manually_ configure the mapping between BlogPost -> Author it would look like this:
module.exports = {
plugins: [...],
mapping: {
'BlogPost.authors': `Author`
}
}
To automate this behavior within the plugin, I imagine I could make use of createTypes and/or createResolvers, but given how ergonomic it is to use mapping, I was hoping plugin authors could somehow harness that interface.
gatsby-source-pg does support relation mapping, but it implements this by using Postgraphile combined with gatsby-source-graphql - Clever!Thank you 馃檱
For mapping in plugin you'd need to use Foreign Key reference in your data. In your example it would be:
{
type: 'BlogPost',
id: 'blog-post-42',
// Each `BlogPost` can have 0 or more `Authors`
- authors: ['author-123', 'author-456'],
+ // ___NODE suffix instruct gatsby to create node link field
+ authors___NODE: ['author-123', 'author-456'],
...
}
{
type: 'Author',
id: 'author-123',
...
}
___NODE suffix can be used for single "foreign node" id or array of ids (like in this example)
Thank you @pieh! 馃檱 I read that section but incorrectly assumed it only worked for single IDs (e.g. author: 'abc123'), but it _should_ work in this case.
My next question is (anticipating edge cases 馃槄): When does inferFromFieldName (or GQL type inference in general) occur?
In the above example, I imagine inference would fail if the name of the field were something other than authors and there weren't any Author nodes created yet, but this _wouldn't_ be a problem if inference occurs _after_ every createNode call as there _would_ be some Author nodes to infer against.
In the above example, I imagine inference would fail if the name of the field were something other than
authorsand there weren't anyAuthornodes created yet, but this _wouldn't_ be a problem if inference occurs _after_ everycreateNodecall as there _would_ be someAuthornodes to infer against.
Graphql schema is created after all plugins created their nodes. This means you can reference nodes that weren't yet created when using ___NODE. (This is needed to create any cross refences). I.e:
// create node that references not yet existing node
createNode({
id: 'foo',
parent: null,
children: []
internal: { type: 'BlogPost', contentDigest: 'contentDigest' },
authors___NODE: [`not-yet-existing-author`],
// your normal data
})
// and create node that was missing:
createNode({
id: 'not-yet-existing-author',
parent: null,
children: []
internal: { type: 'Author', contentDigest: 'contentDigest' },
// back reference to foo
foo___NODE: [`foo`],
// your normal data
})
Is completely fine, as we won't create schema yet.
Fantastic - thank you for the detailed explanation @pieh! I suppose this means Gatsby waits until after all createNode calls before the schema is resolved - makes a lot of sense :+1:
Most helpful comment
Graphql schema is created after all plugins created their nodes. This means you can reference nodes that weren't yet created when using
___NODE. (This is needed to create any cross refences). I.e:Is completely fine, as we won't create schema yet.