I am following a number of resources on the Shopify documentation website and blog around using the stagedUploadsCreate mutation to create a staged URL, however, once I try and use the staged resource URL in a later productCreateMedia mutation, the image is not attached to the product.
Docs: https://shopify.dev/api/examples/product-media
I suspect that there may be an issue with the S3 object being uploaded as private. If I manually try to visit the S3 object URL represented by resourceUrl, I receive an “access denied” response. I suspect that this may be resulting in the Shopify backend being unable to request the file during the productCreateMedia mutation as well. The strange thing is that productCreateMedia does not return any errors, the errors only show on the Shopify admin dashboard. Example below.
Is the URL given to productCreateMedia supposed to be a signed S3 object URL? If so, how would you produce such a URL?
I did notice that when using PUT for the httpMethod for stagedUploadsCreate, the uploadUrl seems to come back as signed or with additional query params, however when using POST that is not the case.
Example Code (stagedUploadsCreate)
const input = [
{
fileSize: file.size.toString(),
filename: file.originalname,
httpMethod: "POST",
mimeType: file.mimetype,
resource: "IMAGE",
}
];
const {body} = await client.query({
data: {
query: `
mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
stagedUploadsCreate(input: $input) {
stagedTargets {
resourceUrl
url
parameters {
name
value
}
}
userErrors {
field
message
}
}
}
`,
variables: {
input
}
}
});
Example Code (upload):
import fetch, {FormData} from 'node-fetch'
const formData = new FormData();
parameters.forEach(({name: paramName, value}) => {
formData.append(paramName, value)
});
formData.append('file', file.buffer, {
contentType: file.mimetype,
filename: file.originalname
});
const response = await fetch(uploadUrl, {
method: 'POST',
body: formData
});
Example Code (productCreateMedia):
const variables = {
id: productId,
media: [
{
originalSource: resourceUrl,
mediaContentType: "IMAGE"
}
]
};
const {body} = await client.query({
data: {
query: `
mutation createProductMedia(
$id: ID!
$media: [CreateMediaInput!]!
) {
productCreateMedia(productId: $id, media: $media) {
media {
mediaContentType
preview {
image {
id
}
}
mediaErrors {
code
details
message
}
mediaWarnings {
code
message
}
}
product {
id
}
mediaUserErrors {
code
field
message
}
}
}
`,
variables
}
});
