Install
$ agentstack add skill-dragnoir-shopify-agent-skills-shopify-functions ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Shopify Functions
When to use this skill
Use this skill when:
- Creating custom discount logic
- Customizing delivery options
- Implementing payment method rules
- Validating cart or checkout
- Building order routing logic
- Extending Shopify's backend behavior
What are Shopify Functions?
Functions are serverless WebAssembly modules that extend Shopify's backend logic. They:
- Run on Shopify's infrastructure
- Execute in milliseconds
- Scale automatically
- Are upgrade-safe
Function Types
| API | Purpose | | ------------------------------ | -------------------------------------- | | Discounts | Product, order, and shipping discounts | | Delivery Customization | Rename, reorder, hide shipping options | | Payment Customization | Filter, reorder payment methods | | Cart & Checkout Validation | Block checkout with errors | | Order Routing | Control fulfillment locations | | Cart Transform | Modify cart contents |
Getting Started
1. Create a Function
# In an existing app
shopify app generate extension
# Select from function types:
# - Delivery customization
# - Product discount
# - Order discount
# - Cart & Checkout Validation
# - etc.
2. Choose a Language
Rust (Recommended) - Best performance, handles large carts
JavaScript - Easier to learn, good for simpler logic
3. Function Structure
extensions/
└── my-discount/
├── src/
│ └── run.rs (or run.js)
├── input.graphql
├── shopify.extension.toml
└── Cargo.toml (for Rust)
Function Anatomy
Configuration (shopify.extension.toml)
api_version = "2025-01"
[[extensions]]
name = "Volume Discount"
handle = "volume-discount"
type = "function"
[[extensions.targeting]]
target = "purchase.product-discount.run"
input_query = "src/run.graphql"
export = "run"
[extensions.build]
command = "cargo wasi build --release"
path = "target/wasm32-wasi/release/volume-discount.wasm"
Input Query (input.graphql)
query RunInput {
cart {
lines {
id
quantity
merchandise {
... on ProductVariant {
id
product {
id
title
hasAnyTag(tags: ["discount-eligible"])
}
}
}
cost {
amountPerQuantity {
amount
currencyCode
}
}
}
}
discountNode {
metafield(namespace: "volume-discount", key: "config") {
value
}
}
}
Product Discount Function (Rust)
// src/run.rs
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
let mut discounts = vec![];
// Parse configuration from metafield
let config: Config = input.discount_node.metafield
.as_ref()
.map(|m| serde_json::from_str(&m.value).unwrap())
.unwrap_or_default();
for line in input.cart.lines {
let quantity = line.quantity;
// Check if eligible for volume discount
if quantity >= config.minimum_quantity {
let merchandise = match &line.merchandise {
input::InputCartLinesMerchandise::ProductVariant(variant) => variant,
_ => continue,
};
// Check for eligible tag
if merchandise.product.has_any_tag {
discounts.push(output::Discount {
targets: vec![output::Target::ProductVariant(
output::ProductVariantTarget {
id: merchandise.id.clone(),
quantity: None,
},
)],
value: output::Value::Percentage(output::Percentage {
value: Decimal::from_str(&config.discount_percentage).unwrap(),
}),
message: Some(format!("{}% volume discount", config.discount_percentage)),
});
}
}
}
Ok(output::FunctionRunResult {
discounts,
discount_application_strategy: output::DiscountApplicationStrategy::FIRST,
})
}
#[derive(Default, serde::Deserialize)]
struct Config {
minimum_quantity: i64,
discount_percentage: String,
}
Product Discount Function (JavaScript)
// src/run.js
// @ts-check
import { DiscountApplicationStrategy } from "../generated/api";
/**
* @param {RunInput} input
* @returns {FunctionRunResult}
*/
export function run(input) {
const config = JSON.parse(
input.discountNode.metafield?.value ??
'{"minimumQuantity": 5, "percentage": "10"}',
);
const discounts = [];
for (const line of input.cart.lines) {
const variant = line.merchandise;
// Check quantity threshold
if (line.quantity >= config.minimumQuantity) {
// Check for eligible products
if (
variant.__typename === "ProductVariant" &&
variant.product.hasAnyTag
) {
discounts.push({
targets: [
{
productVariant: {
id: variant.id,
},
},
],
value: {
percentage: {
value: config.percentage,
},
},
message: `${config.percentage}% volume discount`,
});
}
}
}
return {
discounts,
discountApplicationStrategy: DiscountApplicationStrategy.First,
};
}
Delivery Customization
// Rename, hide, or reorder delivery options
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
let mut operations = vec![];
for method in input.cart.delivery_groups[0].delivery_options.iter() {
// Hide express shipping for heavy orders
if method.title.contains("Express") && cart_weight_exceeds_limit(&input) {
operations.push(output::Operation::Hide(output::HideOperation {
delivery_option_handle: method.handle.clone(),
}));
}
// Rename delivery option
if method.title.contains("Standard") {
operations.push(output::Operation::Rename(output::RenameOperation {
delivery_option_handle: method.handle.clone(),
title: Some("Economy Shipping (5-7 days)".to_string()),
}));
}
}
Ok(output::FunctionRunResult { operations })
}
Payment Customization
// src/run.js
export function run(input) {
const cart = input.cart;
const operations = [];
// Calculate cart total
const total = cart.cost.totalAmount.amount;
// Hide COD for orders over $500
if (parseFloat(total) > 500) {
const codMethod = input.paymentMethods.find((method) =>
method.name.includes("Cash on Delivery"),
);
if (codMethod) {
operations.push({
hide: {
paymentMethodId: codMethod.id,
},
});
}
}
// Reorder payment methods
operations.push({
move: {
paymentMethodId: input.paymentMethods[0].id,
index: 2,
},
});
return { operations };
}
Cart & Checkout Validation
use shopify_function::prelude::*;
use shopify_function::Result;
#[shopify_function_target(query_path = "src/run.graphql", schema_path = "schema.graphql")]
fn run(input: input::ResponseData) -> Result {
let mut errors = vec![];
// Check minimum order value
let total: f64 = input.cart.cost.total_amount.amount.parse().unwrap();
if total = 3) {
operations.push({
expand: {
cartLineId: line.id,
expandedCartItems: [
{
merchandiseId: variant.id,
quantity: line.quantity,
},
{
merchandiseId: "gid://shopify/ProductVariant/FREE_GIFT_ID",
quantity: 1,
},
],
},
});
}
}
return { operations };
}
Testing Functions
Local Testing
# Test with sample input
shopify app function run --path extensions/my-function
# Provide input via stdin
cat input.json | shopify app function run --path extensions/my-function
Sample Input JSON
{
"cart": {
"lines": [
{
"id": "gid://shopify/CartLine/1",
"quantity": 5,
"merchandise": {
"__typename": "ProductVariant",
"id": "gid://shopify/ProductVariant/123",
"product": {
"id": "gid://shopify/Product/456",
"title": "Test Product",
"hasAnyTag": true
}
},
"cost": {
"amountPerQuantity": {
"amount": "10.00",
"currencyCode": "USD"
}
}
}
]
},
"discountNode": {
"metafield": {
"value": "{\"minimumQuantity\": 3, \"discountPercentage\": \"15\"}"
}
}
}
Deployment
# Deploy function with app
shopify app deploy
# Function will be available to configure in admin
Configuration UI
Create an admin UI to configure function settings:
// app/routes/app.discount.jsx
import { authenticate } from "../shopify.server";
import { Form, TextField, Button, Card } from "@shopify/polaris";
export async function action({ request }) {
const { admin } = await authenticate.admin(request);
const formData = await request.formData();
// Create discount with function
await admin.graphql(
`
mutation CreateDiscount($discount: DiscountAutomaticAppInput!) {
discountAutomaticAppCreate(automaticAppDiscount: $discount) {
automaticAppDiscount {
discountId
}
userErrors {
field
message
}
}
}
`,
{
variables: {
discount: {
title: formData.get("title"),
functionId: "YOUR_FUNCTION_ID",
startsAt: new Date().toISOString(),
metafields: [
{
namespace: "volume-discount",
key: "config",
value: JSON.stringify({
minimumQuantity: parseInt(formData.get("minQty")),
discountPercentage: formData.get("percentage"),
}),
type: "json",
},
],
},
},
},
);
return redirect("/app/discounts");
}
Performance Best Practices
- Use Rust for large carts - JavaScript can timeout
- Minimize input query - Only request needed data
- Avoid complex loops - Keep logic simple
- Cache configuration - Parse metafields once
- Test with real data - Test large cart scenarios
CLI Commands Reference
| Command | Description | | -------------------------------- | --------------- | | shopify app generate extension | Create function | | shopify app function run | Test locally | | shopify app function typegen | Generate types | | shopify app deploy | Deploy function |
Resources
- Functions Overview
- Functions API Reference
- Discount Functions
- Delivery Customization
- Payment Customization
- JavaScript for Functions
- Rust for Functions
For checkout UI, see the [checkout-customization](../checkout-customization/SKILL.md) skill.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: dragnoir
- Source: dragnoir/Shopify-agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.