File Upload through GraphQL API - Processing Error

Hi everyone!

I’m setting up a Shopify store to sell software online. The purchased software comes with an activation key/license.

Upon order payment, a Shopify webhook is sent to an AWS Lambda that takes care of:

  1. Gathering the needed customer and order information from the webhook’s payload.
  2. Creating the .xml file containing the activation key.
  3. Uploading the file to Shopify files.
  4. Attaching the uploaded file to a customer list-of-files metafield for it to be displayed in the customer account page.

I’m doing some tests to implement point 3 by simply trying to upload a local file to Shopify using the GraphQL API, but I’m having some issues.

To upload the file I’m using 2 calls to the GraphQL Admin API:

  1. stagedUploadsCreate() mutation call to upload the file.
  2. fileCreate() mutation to create the file asset using the link provided by the staged upload.

Both calls go through correctly and I don’t get any error message in my python script, however, if I keep checked the file page in the Shopify Admin, the file seems to take forever to upload (it’s a 750 bytes xml…), as advised by a pop-up I get in the bottom right corner of the screen, and then the upload promptly fails with a very generic “FileName.xml Processing error” message, with the pop-up replacing the previous one

Any idea of what’s going on?

The following are the specific mutations I’m calling:

mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
  stagedUploadsCreate(input: $input) {
    stagedTargets {
      url
      resourceUrl
      parameters {
        name
        value
      }
    }
    userErrors {
      field
      message
    }
  }
}

variables = {
  "input": 
    {
      "filename": "Prova.xml",
      "fileSize": "1000",
      "httpMethod": "POST",
      "mimeType": "application/xml",
      "resource": "FILE"
    }
}
mutation fileCreate($files: [FileCreateInput!]!) {
  fileCreate(files: $files) {
    files {
      alt
      createdAt
    }
    userErrors {
      field
      message
    }
  }
}

variables = {
  "files": [
    {
      "alt": "License file",
      "contentType": "FILE",
      "originalSource": resource_url
    }
  ]
}

With resource_url extracted by the response from the first mutation.

Thanks!

Hi!

I am having the same problem but I am trying to upload image file.

Is there any progress?

Hi! Unfortunately this was some time ago, so I don’t remember well what exactly I was doing wrong. Most likely I had misunderstood the staged upload process.

After the stagedUploadsCreate() and before the fileCreate() mutations, the file needs to be uploaded to Google clouds through a POST request. To do that, you can use the credentials returned by the stagedUploadsCreate() mutation.

Yes, I have found this example and tried to follow it and Shopify documentation.

This is my index.js file. I am using example image (less than 2 MB).

This is the first step (stagedUploadsCreate() mutation).

This is the second step (uploading file to google cloud and here I am getting Bad Request for this fetch, even I get all credentials returned by the previous step).

This is the third step (fileCreate() mutation).

I think there is a problem with uploading image but I can’t figure out what else to do.
The final result is Processing Error in Content / Files in Shopify.

Sorry I never worked with JS and don’t have much time to dig into it now. If you’re getting bad request on your POST request, then the file is never uploaded to Google cloud, and the fileCreate() will give the processing error because of it.

You have to solve the bad request.

My POST looks something like this in python

files = {
        'key': (None, data["key"]),
        'content-type': (None, data["content_type"]),
        'success_action_status': (None, '201'),
        'acl': (None, data["acl"]),
        'policy': (None, data["policy"]),
        'x-goog-credential': (None, data["goog_credential"]),
        'x-goog-algorithm': (None, data["goog_algorithm"]),
        'x-goog-date': (None, data["goog_date"]),
        'x-goog-signature': (None, data["goog_signature"]),
        'file': (None, file),
    }

    response = requests.post(data["url"], files=files)

where data is the response from stagedUploadsCreate() and file is the file I want to upload.

Hi, thanks for the answer. :slightly_smiling_face:

I think my file isn’t allright. Not the file itself than the way of uploading.
Do I need to convert it to Base64 format or what?
In which format did you sent your file?

Mine is an .xml file simply opened in python using open().

Try to print the full responses you get from your mutations if you haven’t already, the errors will tell you what you are doing wrong.

Thank you. :slightly_smiling_face:

I solved it…
Somehow I put the wrong image (it was larger than 2MB) and I replaced fetch with axios but didn’t work so after a while I returned fetch and figure out that image is too big so I replaced image and again didn’t work. After a while I tried again axios and it worked.
Not sure why, but fetch didn’t give any description for Bad request.

So, in my case, problems were image larger than 2 MB and using fetch.

2 Likes

@NenadR @jacopol I’m having the same issue, I need the client to be able to upload an image file and be saved on the store of the client, but the step of uploading to the cloud never succeeds although I have followed the same steps you mentioned. I’d appreciate some help since I’ve been struggling for a while now.
Here is my question:
https://community.shopify.com/c/graphql-basics-and/filecreate-flow-with-stageduploadscreate-in-graphql/m-p/2187717/highlight/true#M11250

import React, { useCallback, useEffect, useState, useRef } from “react”;
import { json } from “@remix-run/node”;
import { useActionData, useNavigation, useSubmit } from “@remix-run/react”;
import {
Page,
Layout,
Card,
Button,
BlockStack,
Box,
InlineStack,
DropZone,
LegacyStack,
Thumbnail,
Text,
} from “@shopify/polaris”;
import { authenticate } from “../shopify.server”;
import type { ActionFunctionArgs, LoaderFunctionArgs } from “@remix-run/node”;

export const loader = async ({ request }: LoaderFunctionArgs) => {
await authenticate.admin(request);

return null;
};

export const action = async ({ request }: ActionFunctionArgs) => {
const { admin } = await authenticate.admin(request);

const requestBody = await request.text();

const formData = new URLSearchParams(requestBody);
const name = formData.get(“filename”);
const type = formData.get(“filetype”);
const size = formData.get(“filesize”);
const files = [
{
name: name,
type: type,
size: size,
},
];

const prepareFiles = (files: { name: string | null; type: string | null; size: string | null; }) =>
files.map((file) => ({
filename: file.name,
mimeType: file.type,
resource: file.type?.includes(“image”) ? “IMAGE” : “FILE”,
fileSize: file.size?.toString(),
httpMethod: “POST”,
}));

const preparedFiles = prepareFiles(files);

const uploadFileResponse = await admin.graphql(
#graphql mutation stagedUploadsCreate($input: [StagedUploadInput!]!) { stagedUploadsCreate(input: $input) { stagedTargets { resourceUrl url parameters { name value } } userErrors { field message } } } ,
{ variables: { input: preparedFiles } },
);

const uplodeFileJson = await uploadFileResponse.json();

const resourceurl = uplodeFileJson.data.stagedUploadsCreate.stagedTargets[0].resourceUrl;

const fileCreateResponse = await admin.graphql(
`#graphql
mutation fileCreate($files: [FileCreateInput!]!) {
fileCreate(files: $files) {
files {
alt
createdAt
fileErrors {
code
details
message
}
fileStatus
preview {
image {

url
}
status
}
}
userErrors {
field
message
}
}
}`,
{
variables: {
files: {
alt: “Image”,
contentType: “FILE”,
originalSource: resourceurl,
},
},
},
);

const fileCreateJson = await fileCreateResponse.json();

return json({
stagedUpload: uplodeFileJson,
fileCreate: fileCreateJson,
resourceurl: resourceurl
});
};

export default function Index() {
const nav = useNavigation();
const actionData = useActionData();
const submit = useSubmit();
const isLoading = [“loading”, “submitting”].includes(nav.state) && nav.formMethod === “POST”;

useEffect(() => {
if (actionData) {
shopify.toast.show(“Product created”);
}
}, [actionData]);

const [files, setFiles] = useState<File>();
const inputRef = useRef(null);

const handleDropZoneDrop = useCallback(
async (dropFiles: File, acceptedFiles: File, rejectedFiles: File) => {
if (acceptedFiles.length) {
setFiles((prevFiles) => […prevFiles, …acceptedFiles]);
for (const file of acceptedFiles) {
// console.log(file.name);
// console.log(file.size);
// console.log(file.type);
}
}
},
,
);
const validImageTypes = [“image/gif”, “image/jpeg”, “image/png”];

const fileUpload = !files.length && <DropZone.FileUpload />;
const uploadedFiles = files.length > 0 && (

{files.map((file, index) => (
{file.name}{" "} {file.size} bytes
))}
);

const generateProduct = () => {
const filename = files[0]?.name;
const filetype = files[0]?.type;
const filesize = files[0]?.size;
submit({ filename, filetype, filesize }, { replace: true, method: “PUT” });
};

return (



<Layout.Section>



{uploadedFiles}
{fileUpload}


Generate a product



</Layout.Section>



);
};

this code is not working.
Please Provide Correct Code.