Originally published on Nama Studio’s blog, sharing here for the dev community!
If you're looking to streamline your workflows in Shopify, the Mechanic app is an essential tool.
It allows developers to automate tasks, saving time and simplifying operations. Mechanic provides a complete library of pre-built tasks, but we often use it to implement custom logic. When integrating external APIs, it's crucial to properly structure the tasks to manage asynchronous requests, ensure API scope detection, and follow best practices for better reliability and maintainability.
In this article, I’ll show you how we built a Mechanic task that retrieves the Poste Italiane pickup point IDs from ShippyPro, an advanced shipping and logistics platform—and saves them as metafields in Shopify orders.
What this task does
When customers select the local pickup option in Shopify, the pickup point ID is not automatically saved in the order details. This was an issue for one of our clients, who needed to integrate ShippyPro to manage deliveries more efficiently.
ShippyPro is a premium platform that integrates with various carriers, providing tools to automate shipments, tracking, and pickup point management. By using one of their premium APIs, we can retrieve the correct pickup point ID for an order and save it in Shopify’s metafields.
Although ShippyPro and Shopify offer a native integration, due to our client’s ERP structure, orders are transmitted from Shopify to ShippyPro via API, rather than through the standard integration. This custom solution ensures that orders follow the business logic set by the ERP, while still benefiting from the advanced features of ShippyPro.
How does this task work
Here’s a general overview of how our Mechanic task works:
Order Monitoring: The task monitors new Shopify orders with a specific shipping method for local pickup.
API Call to ShippyPro: It makes a request to ShippyPro to retrieve the available pickup points.
Saving the Pickup Point ID: If a pickup point matches the customer’s selection, the ID is saved in the order’s metafields.
Lessons learned
During the creation of this task, we learned a lot about working with external APIs through Mechanic, particularly regarding managing asynchronous responses and correctly detecting API scopes. Let’s go over some of the key lessons we learned.
Managing API Scopes in Mechanic: The Role of Event Previews
One of the main challenges in creating tasks in Mechanic is ensuring that the task has the correct API scopes to perform specific actions. Shopify requires apps to declare these scopes during installation, and Mechanic detects them by analyzing the task's code before execution.
However, there’s a challenge when a GraphQL mutation (such as metafieldsSet) is executed only under specific conditions. If these conditions are not met during the preview, Mechanic might not recognize that the task needs write access to the metafields. This can lead to the task failing during execution because the necessary permissions weren’t granted during installation.
To resolve this issue, we leveraged the concept of event previews. During the preview mode, we provided dummy data to ensure the metafieldsSet mutation was executed even during testing, allowing Mechanic to recognize the need for write permissions (write_orders) before actual execution.
Here’s the code we used:
{% if event.preview %}
{% action "shopify" %}
mutation {
metafieldsSet(
metafields: [
{
ownerId: "gid://shopify/Order/6483826147655",
namespace: "custom",
key: "dropoff_point_id",
type: "single_line_text_field",
value: "1234"
}
]
) {
metafields {
id
}
userErrors {
message
}
}
}
{% endaction %}
{% endif %}
How to work with external APIs in Mechanic
Since operations in Mechanic are executed asynchronously, the results of API calls are not immediately available within the Liquid task that initiated them. To manage this, Mechanic provides a structured approach using the event subscription mechanic/actions/perform, allowing the processing to continue based on the results of the previous task.
To ensure continuity between the steps, we use the meta parameter to pass additional information from the first event to the second, such as the order_id and shipping address, enabling the second event to correctly map the API response to the original order.
The correct approach is to split the task into two steps:
- First event (shopify/orders/create):
- Extract relevant data from the order with GraphQL
- Make the HTTP request to the external API
- Pass extra data using the meta object
- Second event (mechanic/actions/perform):
- Capture the external API response
- Process the response
- Save data to Shopify (e.g., update order metafields)
{% if event.topic == "shopify/orders/create" or event.topic == "mechanic/user/order" %}
{% capture query %}
{% comment %}
Build your query here
{% endcomment %}
{% endcapture %}
{% assign result = query | shopify %}
{% assign order = result.data.order %}
{% assign shipping_address = order.shippingAddress %}
{% if shipping_address %}
{% assign shipping_method = order.shippingLines.edges[0].node.title %}
{% if shipping_method == shipping_point_method %}
{% capture parameters %}
{
"Method": "GetDropOffPoints",
"Params": {
"city": {{ shipping_address.city | json }},
"zip": {{ shipping_address.zip | json }},
"country": "IT",
"couriers": ["CourierName"]
}
}
{% endcapture %}
{% action "http" %}
{
"method": "GET",
"url": {{ endpoint | json }},
"headers": {
"Authorization": {{ ENCODED_API_TOKEN | json }},
"Content-Type": "application/json"
},
"body": {{ parameters | json }},
"meta": {
"order_id": {{ order.id | json }},
"order_shipping_address": {{ shipping_address.address1 | json }}
}
}
{% endaction %}
{% endif %}
{% endif %}
{% elsif event.topic == "mechanic/actions/perform" %}
{% if action.type == "http" %}
{% assign response = action.run.result.body %}
{% assign order_id = action.options.meta.order_id %}
{% assign order_address = action.options.meta.order_shipping_address %}
{% if matching_point_id %}
{% action "shopify" %}
mutation {
metafieldsSet(
metafields: [
{
ownerId: {{ order_id | json }},
namespace: "custom",
key: "dropoff_point_id",
type: "single_line_text_field",
value: {{ matching_point_id | json }}
}
]
) {
metafields {
id
}
userErrors {
message
}
}
}
{% endaction %}
{% endif %}
{% endif %}
Conclusion
Properly structuring Mechanic tasks that interact with external APIs allows you to create powerful and reliable automations. With this solution, orders with local pickup are linked to the correct pickup point through the ShippyPro API, improving logistics efficiency and ensuring a seamless workflow.
If you'd like to learn more about working with Mechanic and external APIs, check out the official app documentation.
Top comments (0)