In early 2024, Shopify introduced a new way to handle files. Instead of creating duplicate file objects for each product, theme, or other resource, the new system uses a single file that can be referenced across a shop. This makes file management more efficient and consistent across a store, reducing the number of duplicates and simplifying the process of updating files.
For example, if you have a product image used across multiple themes, product pages, and marketing materials, you only need to upload and manage one instance of that file. If you need to update the image, you only need to replace it once, and the change will automatically reflect everywhere the file is used.
This change affects how you create and manage files in your app. The following sections provide examples of how to update your app to use the new model.
## Products
Previously, apps would create a [`Product Image`](/docs/api/admin-rest/latest/resources/product-image) for each product using the REST Admin API. With the new files model, apps should use the [`fileCreate`](/docs/api/admin-graphql/latest/mutations/fileCreate) and [`fileUpdate`](/docs/api/admin-graphql/latest/mutations/fileUpdate) mutations to create or update file. The [`fileUpdate`](/docs/api/admin-graphql/latest/mutations/fileUpdate) mutation can be used to either updated the contents of a file, or associate it to a product. When working directly with products, apps should use the [`productCreate`](/docs/api/admin-graphql/latest/mutations/productCreate), [`productUpdate`](/docs/api/admin-graphql/latest/mutations/productUpdate), or [`productSet`](/docs/api/admin-graphql/latest/mutations/productSet) mutations to associate files with corresponding products.
This change reflects our move towards a more streamlined approach where file information and associations are managed through the File APIs rather than through product-specific endpoints.
For example, if an app wanted to add an image to a product, it would previously use the following REST API call:
Now, the same operation can be achieved [asynchronously](#asynchronous-file-processing) using the following GraphQL Admin API calls:
You can then poll the file fileStatus using this query until it's ready:
The `status` field will be one of: `UPLOADED`, `PROCESSING`, `READY`, or `FAILED`. Once the status is `READY`, you can proceed with using the file.
Next, you can associate the file with the product using the [`productSet`](/docs/api/admin-graphql/latest/mutations/productSet) mutation:
### Alternative approach using `fileUpdate`
You can also update an existing file's references using the [`fileUpdate`](/docs/api/admin-graphql/latest/mutations/fileUpdate) mutation:
> Note:
> The `fileType` parameter in the file GID is the type of file object that you are updating. This is either `MediaImage`, `Video`, `GenericFile`, `ExternalVideo`, or `Model3d`.
### REST API changes to support migration
> Note:
> As of version `2025-01`, the REST Admin API for the [product image resource](https://shopify.dev/docs/api/admin-rest/2024-10/resources/product-image) will return a files image GID instead of a product image GID.
This change helps simplify migration from REST to GraphQL when working with product images created before the unified files ID system.
#### GID Format Changes
| Version | GID Format | Example |
|---------|------------|---------|
| Previous | ProductImage GID | `"gid://shopify/ProductImage/43701248884792"` |
| New (`2025-01`) | mediaImage GID | `"gid://shopify/MediaImage/12379812379123"` |
#### Key Benefits
- Direct use of returned files image GID in GraphQL queries and mutations
- No ID translation or lookup needed
- Ability to re-ruse existing files across different products
#### Version Compatibility
- Only affects the `2025-01` API version and newer
- Older versions will continue returning ProductImage GID
### Deprecated APIs and their recommended replacements
The following product-centric files APIs are being retired:
| Deprecated (2024-10) | Replacement |
|--------------------------|-------------|
| `productAppendImages` | [`fileCreate`](/docs/api/admin-graphql/latest/mutations/fileCreate) then [`productCreate`](/docs/api/admin-graphql/latest/mutations/productCreate) or [`productSet`](/docs/api/admin-graphql/latest/mutations/productSet) |
| `productDeleteImages` | [`fileDelete`](/docs/api/admin-graphql/latest/mutations/fileDelete) |
| `productImageUpdate` | [`fileUpdate`](/docs/api/admin-graphql/latest/mutations/fileUpdate) |
### Upcoming API Changes
We are transitioning from product-centric files APIs to file-centric APIs. Here's how the current APIs will map to the new system:
| Current Product-Centric API | Future File-Centric API |
|----------------------------|-------------------------|
| `productCreateMedia` | [`fileCreate`](/docs/api/admin-graphql/latest/mutations/fileCreate) then [`productCreate`](/docs/api/admin-graphql/latest/mutations/productCreate) or [`productSet`](/docs/api/admin-graphql/latest/mutations/productSet) |
| `productDeleteMedia` | [`fileDelete`](/docs/api/admin-graphql/latest/mutations/fileDelete) |
## Collections
When working with collections, the process is slightly different from products. Because collections are not yet fully migrated to use files references, you need to pass files to collection mutations directly.
### Creating Collections with files
To add files to a new collection, follow these steps:
> Note: If you're using an external image URL and don't need to store the file in Shopify's CDN, you can skip directly to step 3 and use your external URL in the `src` field.
1. Create or obtain the file's GID using [`fileCreate`](/docs/api/admin-graphql/latest/mutations/fileCreate):
2. Query the file to get the CDN URL:
3. Create the collection using the image URL:
### Updating Collections with files
The process for updating a collection's files follows the same steps:
1. Create or obtain the mediaImage GID using [`fileCreate`](/docs/api/admin-graphql/latest/mutations/fileCreate) or querying the `file` object
2. Query the `file` to get its corresponding CDN URL
3. Update the collection using the image URL:
## Asynchronous file processing
A key change to the files system is the shift from immediate to asynchronous processing. Updates now take effect after the mutation completes, improving efficiency and reducing the risk of concurrent update conflicts.
It's important to optimize your approach to avoid unnecessary polling when working with multiple images. Rather than polling each file individually, use a single GraphQL query to check the status of all files at once.