Skip to content

Instantly share code, notes, and snippets.

@celsowhite
Last active April 29, 2024 07:42
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save celsowhite/2e890966620bc781829b5be442bea159 to your computer and use it in GitHub Desktop.
Save celsowhite/2e890966620bc781829b5be442bea159 to your computer and use it in GitHub Desktop.
Shopify Files API
/*------------------------
Libraries
------------------------*/
const axios = require("axios");
const fs = require("fs");
const FormData = require("form-data");
/*------------------------
Download the file.
Good article on how to download a file and send with form data - https://maximorlov.com/send-a-file-with-axios-in-nodejs/
------------------------*/
const file = await fs.readFile("./your-image.jpg"); // This can be named whatever you'd like. You'll end up specifying the name when you upload the file to a staged target.
const fileSize = fs.statSync("./your-image.jpg").size; // Important to get the file size for future steps.
/*------------------------
Create staged upload.
---
Shopify sets up temporary file targets in aws s3 buckets so we can host file data (images, videos, etc).
If you already have a public url for your image file then you can skip this step and pass the url directly to the create file endpoint.
But in many cases you'll want to first stage the upload on s3. Cases include generating a specific name for your image, uploading the image from a private server, etc.
------------------------*/
// Query
const stagedUploadsQuery = `mutation stagedUploadsCreate($input: [StagedUploadInput!]!) {
stagedUploadsCreate(input: $input) {
stagedTargets {
resourceUrl
url
parameters {
name
value
}
}
userErrors {
field
message
}
}
}`;
// Variables
const stagedUploadsVariables = {
input: {
filename: "example.jpg",
httpMethod: "POST",
mimeType: "image/jpeg",
resource: "FILE", // Important to set this as FILE and not IMAGE. Or else when you try and create the file via Shopify's api there will be an error.
},
};
// Result
const stagedUploadsQueryResult = await axios.post(
`${your_shopify_admin_url}/graphql.json`,
{
query: stagedUploadsQuery,
variables: stagedUploadsVariables,
},
{
headers: {
"X-Shopify-Access-Token": `${your_shopify_admin_token}`,
},
}
);
// Save the target info.
const target =
stagedUploadsQueryResult.data.data.stagedUploadsCreate.stagedTargets[0];
const params = target.parameters; // Parameters contain all the sensitive info we'll need to interact with the aws bucket.
const url = target.url; // This is the url you'll use to post data to aws or google. It's a generic s3 url that when combined with the params sends your data to the right place.
const resourceUrl = target.resourceUrl; // This is the specific url that will contain your image data after you've uploaded the file to the aws staged target.
/*------------------------
Post to temp target.
---
A temp target is a url hosted on Shopify's AWS servers.
------------------------*/
// Generate a form, add the necessary params and append the file.
// Must use the FormData library to create form data via the server.
const form = new FormData();
// Add each of the params we received from Shopify to the form. this will ensure our ajax request has the proper permissions and s3 location data.
params.forEach(({ name, value }) => {
form.append(name, value);
});
// Add the file to the form.
form.append("file", file);
// Headers
const headers = {
...form.getHeaders(), // Pass the headers generated by FormData library. It'll contain content-type: multipart/form-data. It's necessary to specify this when posting to aws.
};
if (url.includes("amazon")) {
// Need to include the content length for Amazon uploads. If uploading to googleapis then the content-length header will break it.
headers["Content-Length"] = fileSize + 5000; // AWS requires content length to be included in the headers. This may not be automatically passed so you'll need to specify. And ... add 5000 to ensure the upload works. Or else there will be an error saying the data isn't formatted properly.
}
// Post the file data to shopify's aws s3 bucket. After posting, we'll be able to use the resource url to create the file in Shopify.
await axios.post(url, form, {
headers
});
/*------------------------
Create the file.
Now that the file is prepared and accessible on the staged target, use the resource url from aws to create the file.
------------------------*/
// Query
const createFileQuery = `mutation fileCreate($files: [FileCreateInput!]!) {
fileCreate(files: $files) {
files {
alt
}
userErrors {
field
message
}
}
}`;
// Variables
const createFileVariables = {
files: {
alt: "alt-tag",
contentType: "IMAGE",
originalSource: resourceUrl, // Pass the resource url we generated above as the original source. Shopify will do the work of parsing that url and adding it to files.
},
};
// Finally post the file to shopify. It should appear in Settings > Files.
const createFileQueryResult = await axios.post(
`${your_shopify_admin_url}/graphql.json`,
{
query: createFileQuery,
variables: createFileVariables,
},
{
headers: {
"X-Shopify-Access-Token": `${your_shopify_admin_token}`,
},
}
);
@Dev-Waheed
Copy link

Hi @celsowhite Hope You Are Doing Well I Need Little Help Regarding Multiple Images/Files Upload at once Via Shopify Files Api.
Any Help regarding My query Will Be Appreciated.
Thanks In Advance.

@santhoshnsscoe
Copy link

Hi @celsowhite, we are uploading image using express fileupload and then pushing the file to shopify api files. We are getting error TypeError: source.on is not a function. If we don't use const FormData = require("form-data"); the image is getting uploaded. But, not listing in the files section. There it shows Processing Error

@walid-chekkouri
Copy link

@santhoshnsscoe @Dev-Waheed I'm facing the same issue with processing error displayed in the files dashboard, how can I fix it? The staging part works fine but the post request part returns bad request always.

@santhoshnsscoe
Copy link

@walid-chekkouri we fixed this issue by first saving the uploaded file in our server and then read the file using fs.readFileSync and then sending this data as formdata file field.

@walid-chekkouri
Copy link

walid-chekkouri commented Aug 22, 2023

@santhoshnsscoe That's just a workaround, it should work without the need to save the file on your server that's why they generate the temporary url so you can save it to the cloud instead. I think they need to offer more documentation and support regarding this I have came across many questions and articles where people are having the same issue with it.

But would you kindly share a code snippet of your approach, I'd like to compare it to see if we correctly have used it or not?

@santhoshnsscoe
Copy link

@walid-chekkouri added the sample code to share code snippet. https://www.coderstool.com/cs/09a6B9

@walid-chekkouri
Copy link

Thanks @santhoshnsscoe I appreciate it, it's strange tough I still have the same error even with your solution!
I double checked the access permissions and I do have write_files but still same issue, it's weird

@santhoshnsscoe
Copy link

@walid-chekkouri sorry don't know what to do. Have you tried with different images? All images are not getting uploaded ? For us, some images don't get uploaded, as they have different encoding or mime type.

@walid-chekkouri
Copy link

Thanks @santhoshnsscoe, yeah I tried with different image files same issue, thanks for the help tough I appreciate it.

@tempestrock
Copy link

Thank you so much for this!
I've been searching a lot for a solution on how to upload an image to Shopify, and it seems this is the only place on the whole internet which provides it.

@EcomGraduates
Copy link

does anyone have an example for video?

@shrey426
Copy link

is there a way to move these files to the products section of the Shopify dashboard?

@mohitvhits
Copy link

mohitvhits commented Apr 29, 2024

I have created an app for Shopify app using Remix. when my custom app uploads button through image upload. then the image shows the status uploaded but not the processing and ready state, so please help me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment