githubEdit

Running bulk operation mutations

Run Shopify bulk operation mutations from a Mechanic task using staged JSONL uploads.

Shopify's bulkOperationRunMutation runs one mutation many times, using a JSONL file where each line contains the variables for one mutation execution. Mechanic can monitor the resulting Shopify bulk operation and return the completed JSONL result to the same task using mechanic/shopify/bulk_operation.

circle-exclamation

When to use this pattern

Use a bulk operation mutation when you need to run the same Shopify-supported and Mechanic-allowed mutation many times, and your task can wait for an asynchronous result. This is useful for high-volume imports or updates where normal one-by-one GraphQL actions would be slower or harder to throttle.

Do not use this pattern when each mutation depends on the result of the previous one, when the order of execution matters, or when your task needs the result immediately in the same run.

Shopify allows most Admin API mutations in bulkOperationRunMutation, except recursive bulk operation mutations such as bulkOperationRunMutation and bulkOperationRunQuery themselves. Shopify also limits the mutation string to one connection field. Mechanic still applies the Shopify action's mutation safety checks; if a mutation is not allowed in Mechanic, the action run will fail with an error.

circle-info

Mechanic's HTTP action uses file generators for multipart uploads, and generated files have a 20MB per-file limit. Shopify's bulk import JSONL limit may be higher than that, so split large imports into batches that fit Mechanic's generated-file limit.

How the flow works

A bulk operation mutation has one extra step that bulk operation queries do not have: Shopify needs the variables in a staged JSONL file before the bulk mutation can start.

In Mechanic, this usually uses action result events to move through the workflow:

  1. A shopify action calls stagedUploadsCreate with resource: BULK_MUTATION_VARIABLES.

  2. A mechanic/actions/perform run uploads the JSONL variables file using the HTTP action.

  3. Another mechanic/actions/perform run calls bulkOperationRunMutation, using the staged upload path from the first step.

  4. A final mechanic/actions/perform run confirms Shopify accepted the bulk mutation.

  5. A mechanic/shopify/bulk_operation run receives the completed bulk operation and reads bulkOperation.objects.

Shopify requires the bulk mutation to finish within 24 hours. If a job fails or gets too large, reduce the input size and run multiple batches.

Permissions

This example declares read_products and write_products explicitly:

Mechanic can often infer permissions from Shopify actions, but staged JSONL files are uploaded directly to Shopify and are not visible to Mechanic's permission scanner. Explicit permissions are safest for bulk mutations, especially for generic mutations like tagsAdd(id: $id) or metafieldsSet(metafields: $metafields), where the target resource IDs only appear in the JSONL file.

Try it now

Use the importable example below if you need to test the full staged upload and bulk mutation flow.

When writing your own task from a blank task, start typing boilerplate:bulk_operation_mutation in the code editor to insert starter code for this full pattern. Use boilerplate:staged_upload_jsonl if you only need the staged upload portion.

After importing it:

  1. Run the task manually.

  2. Watch the task move through three mechanic/actions/perform stages: create_staged_upload, run_bulk_mutation, and start_bulk_mutation.

  3. Wait for Shopify to finish the bulk operation.

  4. Open the later mechanic/shopify/bulk_operation run and inspect the logs for line-level results.

chevron-rightImportable task exporthashtag

The important pieces

The staged upload is created with Shopify's BULK_MUTATION_VARIABLES resource:

The JSONL file contains one variables object per line:

Each top-level JSONL key must match a variable in the mutation string. In this example, each line has a product key because the mutation declares $product.

The HTTP action uploads that JSONL as a multipart file. The files["file"] key is intentional: Shopify staged uploads expect the uploaded file field to be named file. Do not manually set a multipart Content-Type header; Mechanic sets the multipart boundary for you.

The stagedUploadPath value comes from the staged upload parameter named key.

After the upload succeeds, the task can start the bulk mutation:

When Shopify completes the operation, Mechanic invokes the same task with mechanic/shopify/bulk_operation. Use bulkOperation.type to distinguish mutation results from query results:

For bulk mutations, each result object is the GraphQL response for one JSONL input line. A completed bulk operation can still contain per-line errors or userErrors; check those fields before treating a line as successful. Shopify can include __lineNumber in the output to help connect a result back to the input line.

Last updated

Was this helpful?