A space to discuss GraphQL queries, mutations, troubleshooting, throttling, and best practices.
I'm trying to upload various files(image, video, etc) with the fileCreate mutation after staging the upload with stagedUploadsCreate.
I saw https://community.shopify.com/c/shopify-apis-and-sdks/stageduploadscreate-graphql-api-creates-file-w... mentioned that the parameters need to be dynamically added, so I was wondering how I should do that with GraphQL. I am able to get stagedUploadsCreate to return a response with fileStatus of UPLOADED.
When I attempt the fileCreate mutation, I don't receive any errors within the response. I am only able to see that the file runs into a processing error when viewing files on my Shopify store in the dashboard.
Query for stagedUploadsCreate:
mutation ($input: [StagedUploadInput!]!) { stagedUploadsCreate(input: $input) { stagedTargets { parameters { name value } resourceUrl url } userErrors { field message } } } input = { filename: "image.jpg", mimeType: "image/jpeg", httpMethod: "PUT", //I've also tried POST resource: IMAGE }
mutation ($input: [FileCreateInput!]!) { fileCreate(files: $input) { files { alt createdAt fileErrors fileStatus ... on MediaImage { id } ... on Video { id } ... on GenericFile { id url } } userErrors { field message } } } input = [{ originalSource: image url from stagedUploadsCreate, contentType: "IMAGE" }]
Hey @ninainventables,
Thanks for providing some excellent examples and references in your post, along with your questions. I did some digging, testing, and wanted to pass on a few insights. It appears from the post that you're using the correct mutations, `stagedUploadsCreate` and `fileCreate`, but there are a few additional steps that I wanted to highlight:
I'd also recommend testing the full workflow with an API client, eg. Postman or Insomnia, and trying a few different valid media types. When testing on my end from the docs and guide shared above, there were no errors or issues through the process.
Hope this helps provide some clarity and resources to work from - Cheers!
@awwdam | Shopify Developer Support
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
@ShopifyDevSup I'm having the same issue faced by @ninainventables I'd really appreciate it if you can help, I read somewhere that there needs to be a third step between the stagedUploadsCreate mutation and the fileCreate mutation, which is to prepare a form and commence uploading!
Here are the resources I checked:
Link1
Link2
Link3
I have tried it but it never works for me at all.
The staged response does indeed return a url and paameters but that's the last successful step that happens.
Here is what I'm trying to achieve, the client when using my Shopify app has a dropzone where he can upload images, I'm trying to save those images to the files in his store, I have tried @ninainventables 's approach but I keep getting the processor error when I check the files dashboard of the store.
Here is my client side code:
The following is supposed to communicate with the server the image file the client is uploading.
Here is the code in the server side:
app.post('/api/upload-endpoint', async (req, res) => {
try {
if (!req.files || !req.files.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
const uploadedFile = req.files.file;
const formData = new FormData();
// Define the stagedUploadsCreate mutation input
const input = [
{
resource: 'IMAGE',
httpMethod: 'POST',
mimeType: uploadedFile.mimetype,
filename: uploadedFile.name,
fileSize: uploadedFile.size.toString(),
},
];
// Create a staged upload URL using stagedUploadsCreate mutation
let stagedUploadResponse = await fetchUploadImageFile(res.locals.shopify.session, input);
const stagedTarget = stagedUploadResponse.body.data.stagedUploadsCreate.stagedTargets[0];
//passing the parameters
stagedTarget.parameters.forEach(({name, value}) =>
{
formData.append(name, value);
});
//Passing the file as argument (should be last), I tried to pass only the file instead but that didn't change anything.
formData.append('file', uploadedFile.data, {
filename: uploadedFile.name,
contentType: uploadedFile.mimetype,
});
// Now, we upload the file
const fileCreateResponse = await fetch(stagedTarget.url, {
method: 'POST',
body: formData,
});
let myArrayOfFiles = new Array();
const paramObj =
{
"alt": "Image Alt!",
"originalSource": stagedTarget.resourceUrl,
"contentType": "IMAGE",
};
myArrayOfFiles.push(paramObj);
const myResultIs2 = await fetchUploadImageFileContinue(res.locals.shopify.session, myArrayOfFiles);
} catch (error) {
console.error('Error handling file upload:', error);
res.status(500).json({ error: 'An error occurred' });
}
});
const formData = new FormData();
formData.append('file', filesArray.current[0]);
const response = await fetch2('/api/upload-endpoint', {
method: 'POST',
body: formData,
});
Here are the codes for the mutations:
export default async function fetchUploadImageFile(
session, myArr
) {
const client = new shopify.api.clients.Graphql({ session });
try {
const myData = await client.query({
data: {
"query": `mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
stagedUploadsCreate(input: $input) {
stagedTargets {
url
resourceUrl
parameters {
name
value
}
}
}
}`,
"variables":
{
"input": myArr
},
},
});
return myData;
} catch (error) {
if (error instanceof GraphqlQueryError) {
throw new Error(
`${error.message}\n${JSON.stringify(error.response, null, 2)}`
);
} else {
throw error;
}
}
}
export default async function fetchUploadImageFileContinue(
session, filesArray
) {
const client = new shopify.api.clients.Graphql({ session });
try {
const myData = await client.query({
data: {
"query": `mutation fileCreate($files: [FileCreateInput!]!) {
fileCreate(files: $files) {
files {
alt
createdAt
}
}
}`,
"variables": {
"files": filesArray
},
},
});
return myData;
} catch (error) {
if (error instanceof GraphqlQueryError) {
throw new Error(
`${error.message}\n${JSON.stringify(error.response, null, 2)}`
);
} else {
throw error;
}
}
}
Hey @Rachid_98 - thanks for your patience on this. I can't say for certain why this is failing without a bit more info (for example an X-Request-ID from the Shopify response and a relative date/time stamp for the issue), but one thing that could potentially cause an issue like this is the content-type value that's being set in the parameter form for the API call where the product is uploaded after you create the staged upload path (if I'm reading your shared code correctly).
I would confirm that the content-type is set to the correct value for the file being upload (for example: image/jpg, etc.) within the multi-part header params. There is a good breakdown on how this should be formatted here on shopify.dev.
If this doesn't help/resolve the issue, we're still happy to help out. I would recommend reaching out to our Partner Support team directly through the Support tool in your Partner Dashboard if you do encounter further issues. They can act as a liaison between you and some of our more technical teams to troubleshoot further. If you do reach out to Partner Support directly, I would share this thread with them and point to my message for extra context as well as include any relevant logging details that you can (the raw API calls you're making, any X-Request-IDs you receive from Shopify API response and their date/timestamps as well as the code you've already shared here).
I can't guarantee exactly what the next steps would be, but it may help with getting some faster assistance to reach out to Partner Support directly.
Hope this helps and hope to hear from you soon.
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hi @ShopifyDevSup Thanks for the reply, this has been an issue we're struggling with for a while.
Here is the 'X-Request-Id': [ '211ace7d-518f-438a-87d7-7fbb4146cbf0' ] and date:
Date: [ 'Wed, 23 Aug 2023 18:36:58 GMT' ],returned in the headers of the response from the stagedUploadsCreate mutation.
The content-type is 'image/jpeg' so it's correct, all parameters returned from the above mutation are correct. The issue I strongly believe comes from post request, perhaps something is missing and I don't know what is it but you mentioned that the code is correct.
I already contacted the support but no luck so far in finding the issue, I'd be happy to schedule a quick call where I can show you the live demo, I already shared more info with the support, my ticket ID is 40969193. I'd be appreciative if you can help me solve it, I noticed that some other developers are running into the same issue too.
Hey @Rachid_98 , thanks for following up.
Since you're working with multiple files, using stagedUploadsCreate there isn't currently a workflow to upload multiple media files instead of one.
One option to do this at scale though is if you have the files hosted URL (either the staged URL or hosted externally) then you can use bulk operations to create the products
Here's an example of the variables
https://shopify.dev/docs/api/usage/bulk-operations/imports#create-a-jsonl-file-and-include-graphql-v...
combined that with the 'Create a product with a new media' example near the bottom of this page for the ProductCreate mutation: https://shopify.dev/docs/api/admin-graphql/2023-07/mutations/productcreate#examples-Create_a_product...
Hope it helps!
- Kyle
Developer Support @ Shopify
- Was this reply helpful? Click Like to let us know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog