Reputation: 20107
Using Gatsby.js, I want to transform a number of static files into a hierarchy. One aspect of this hierarchy is that one "executable" has many files produced by that executable. My GraphQL schema for an executable is:
exports.createSchemaCustomization = ({ actions: {createTypes}, schema }) => {
createTypes([
schema.buildObjectType({
name: "CondaExecutable",
fields: {
wrappers: "[File]",
name: "String!",
path: "String!",
publicURL: "String!",
},
interfaces: ["Node"],
}),
])
}
Then, I want to add multiple files to the wrapper
field of my new object, I try to do that in createPages
, with reference to the foreign key section in the Gatsby docs:
exports.createPages = async ({ graphql, actions, getNode, createContentDigest, createNodeId}) => {
const { createNode, createNodeField, createPage, createParentChildLink } = actions
const result = await graphql(`
{
allFile(filter: {sourceInstanceName: {in: ["Wrappers", "Definitions"]}}) {
edges {
node {
id
relativePath
extension
publicURL
}
}
}
}
`)
await Promise.all(result.data.allFile.edges.map(async ({ node }) => {
// Now create the node for the single file within that package
const exeId = createNodeId(...);
let exe = getNode(exeId);
if (!exe) {
exe = {
id: exeId,
name: stem,
path: node.relativePath.split('.')[0],
publicURL: exeUrl,
parent: versionId,
wrappers: [],
internal: {
type: "CondaExecutable",
contentDigest: node.relativePath
}
};
await createNode(exe);
}
// Link the executable to the wrapper
const wrappers = exe.wrappers || [];
wrappers.push(node.id)
createNodeField({node: exe, name: 'wrappers___NODE', value: wrappers});
}));
}
Unfortunately this code doesn't work. I get the error Cannot return null for non-nullable field File.id
. In any case, I'm not surprised this is wrong, because I don't really know what I'm doing here.
How can I make a relationship between my own custom type, and many File
s?
Upvotes: 1
Views: 812
Reputation: 20107
As explained much better in this section of the documentation, the way you implement a foreign key relationship differs depending on how you defined your type. Only if you're using automatic type inference do you use the ___NODE
syntax. If you have a custom type defined in GraphQL instead of using createTypes()
, you can use the @link
directive for this purpose.
If you're using the third option, createTypes()
, to define your custom types as I am, you need to instead implement resolve()
for your foreign keys. In my one-to-many case that means:
schema.buildObjectType({
name: "CondaExecutable",
fields: {
wrappers: {
type: "[File]",
resolve(source, args, context, info){
return context.nodeModel.getNodesByIds({
ids: source.wrappers, // This matches the name of the field we're currently inside
type: 'File' // This matches the type of the wrappers field
})
}
},
name: "String!",
path: "String!",
publicURL: "String!",
},
interfaces: ["Node"],
})
If you instead have a one-to-one relationship, your resolve function would look like:
resolve(source, args, context, info){
return context.nodeModel.getNodeById({
id: source.wrapper,
type: 'File'
})
}
Then, to link the nodes in createPages
, you don't have to use the ___NODE
syntax, but you do have to use createNodeField
with an updated array each time you add a new child:
const wrappers = exe.wrappers || [];
wrappers.push(node.id)
createNodeField({node: exe, name: 'wrappers', value: wrappers});
Upvotes: 1