Afternoon folks,
I’m trying to use the new Files API, in concert with the stagedUploadsCreate mutation, to allow our client to upload images through an Admin app that we are creating for them. The image data is going to be coupled to some other custom product metadata, which we’ll handle in the background - hence not wanting to use the out-of-the-box Files interface.
I’ve reviewed this blog post, as well as the Files API release notes and the fileCreate mutation docs, so I think I understand the process correctly:
- Use the stagedUploadsCreate mutation to upload the file metadata (name, type, size), and receive an upload URL and metadata parameters
- Use the URL and metadata params from the stagedUploadsCreate response to upload the file to the destination (a Shopify-owned S3 bucket)
- Use the fileCreate mutation to create a File (MediaImage) representation of the uploaded image, which will then be available to query and get the Shopify edge-cached URL for
My implementation of this in my React (Typescript) app looks something like this:
// given imageFile, which is a File that has been selected by the user using the Polaris DropZone component
const STAGED_UPLOADS_CREATE = gql`
mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
stagedUploadsCreate(input: $input) {
stagedTargets {
resourceUrl
url
parameters {
name
value
}
}
userErrors {
field
message
}
}
}
`;
const FILE_CREATE = gql`
mutation fileCreate($files: [FileCreateInput!]!) {
fileCreate(files: $files) {
files {
alt
createdAt
... on MediaImage {
id
mediaErrors {
code
details
message
}
fileStatus
status
image {
originalSrc
transformedSrc
}
}
}
userErrors {
code
field
message
}
}
}
`;
const [
stagedUploadsCreate,
{ error: stagedUploadsCreateError },
] = useMutation(STAGED_UPLOADS_CREATE);
const [fileCreate, { error: fileCreateError }] = useMutation(FILE_CREATE);
const { data: stagedUploadsCreateData } = await stagedUploadsCreate({
variables: {
input: {
resource: "IMAGE",
filename: imageFile.name,
mimeType: imageFile.type,
fileSize: imageFile.size.toString(),
httpMethod: "POST",
},
},
});
console.log(stagedUploadsCreateData);
const {
stagedUploadsCreate: {
stagedTargets: [{ url, parameters, resourceUrl }],
},
} = stagedUploadsCreateData;
const formData = new FormData();
parameters.forEach(({ name: paramName, value }: any) =>
formData.append(paramName, value),
);
formData.append("file", imageFile);
await axios.post(url, formData);
const { data: fileCreateData } = await fileCreate({
variables: {
files: [{ contentType: "IMAGE", originalSource: resourceUrl }],
},
});
This all goes fine - I stepped through the network requests, and I get a 201 response code back from the file upload request, and then I can see that the fileCreate mutation runs successfully. In the status field of the returned object, I can see that the status is “UPLOADED”.
However, when I go to the Files UI in Shopify Admin, I get the error:
3 files failed to upload> The rest of your files uploaded successfully. View failed files for more details.
(As you might guess, I’ve tried this with 3 different files so far!)
When I query the files resource via the API to get a closer look, I see the follow response:
As far as I can tell, I’m using the API as intended. Unfortunately, there doesn’t seem to be a great deal in the way of documented examples for using this API at the moment. Can anyone point me in the right direction please? I’m sure I’m making some small mistake, but just can’t for the life of me figure out what.
Thanks in advance, and have a great day folks!
James.
