Since Gatsby 4 doesn't accept remoteFileNodes to be created within the createResolvers
API inside gatsby-node.js
, i'm looking for another solution to create File's from our remote (graphql) source.
Creating files on the upper level of an object works just fine, however i can't find a way to create these files inside nested objects in my schema.
Although there is a File
object created with the provided name inside Sections, all the data inside of it results in null. The given URL is checked and is valid.
The following code is inside my gatsby-node.js
file:
sourceNodes
exports.sourceNodes = async ({ actions, createContentDigest, createNodeId }) => {
const { createNode } = actions;
const { data } = await client.query({
query: gql`
query {
PageContent {
id
main_image_url
blocks {
title
sections {
title
nested_image_url
}
}
}
}
`,
});
data.PageContent.forEach((pageContent) => {
createNode({
...pageContent,
id: createNodeId(`${PAGE_CONTENT}-${pageContent.id}`),
parent: null,
children: [],
internal: {
type: PAGE_CONTENT,
content: JSON.stringify(pageContent),
contentDigest: createContentDigest(pageContent),
}
})
});
return;
};
onCreateNode
exports.onCreateNode = async ({
node,
actions: { createNode, createNodeField },
createNodeId,
getCache,
}) => {
if (node.internal.type === PAGE_CONTENT) {
This works just fine
if (node.main_image_url) {
const fileNode = await createRemoteFileNode({
url: node.main_image_url,
parentNodeId: node.id,
createNode,
createNodeId,
getCache,
});
if (fileNode) {
createNodeField({ node, name: "main_image", value: fileNode.id });
}
}
But this won't
if (node.blocks && node.blocks.length > 0) {
node.blocks.map(async({ sections }) => {
if (sections.length > 0) {
sections.map(async(section) => {
if (section.nested_image_url) {
const fileNode = await createRemoteFileNode({
url: section.nested_image_url,
parentNodeId: node.id,
createNode,
createNodeId,
getCache,
});
if (fileNode) {
createNodeField({ node, name: "nested_image", value: fileNode.id });
}
}
})
}
})
}
}
};
createSchema
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions;
createTypes(`
type PageContent implements Node {
main_image: File @link(from: "fields.main_image")
blocks: [Block]
}
type Block {
sections: [Section]
}
type Section {
nested_image: File @link(from: "fields.nested_image")
}
`);
};
Would be really grateful if someone has a clue!
Meanwhile I've come to a solution, which includes the use of the onCreateNode
and createSchemaCustomization
API's
// Single Node
const fileNode = await createRemoteFileNode({
url: node.image,
parentNodeId: node.id,
createNode,
createNodeId: (id) => `${node.unique_identifier_prop}-image`,
getCache,
});
// Array
await Promise.all(
node.images.map((url, index) => (
createRemoteFileNode({
url: url,
parentNodeId: node.id,
createNode,
createNodeId: id => `${node.unique_identifier_prop}-images-${index}`,
getCache,
})
))
)
First you can create a FileNode to your own liking, and instead of the API's createNodeId function. We replace it by a unique and retrievable identifier, so we can locate the File Node in our Schema.
exports.createSchemaCustomization = async({ actions, schema }) => {
const { createTypes } = actions;
const typeDefs = [
schema.buildObjectType({
name: `**target_typename**`,
fields: {
// Single Node
imageFile: {
type: 'File',
resolve: (source, args, context, info) => {
return context.nodeModel.getNodeById({
id: `${source.unique_identifier_prop}-image`,
type: 'File',
})
}
},
// Array
imageFiles: {
type: '[File]',
resolve: (source, args, context, info) => {
const images = source.images.map((img, index) => (
context.nodeModel.getNodeById({
id: `${source.unique_identifier_prop}-images-${index}`,
type: 'File',
})
))
return images
}
},
}
})
];
createTypes(typeDefs)
};
In the createSchemaCustomization
we now can define our custom type with the buildObjectType
function provided by schema, which is available in this API.
In the resolver, we can retrieve the node's values with the source
parameter, which holds our unique_identifier_prop
. Now, with the context
parameter, we can use the getNodeById
function to retrieve the File Node that is bound to our provided ID. Finally, we can return the found File Node and attach it to our Node.