App functions: the 6 declarative function types
App functions let your app reshape carts, shipping, payments, deliveries, orders, and discounts without owning a server. You ship a tiny JavaScript file, the platform compiles it to a 3 KB portable bytecode module via Javy, and the host runs it inline during checkout. Functions are deterministic, fast, and isolated.
There are six function types. This guide explains what each one does, where it runs, and how to test it.
The six types at a glance
cart_transform— rewrite line items (price, properties, merge/split).shipping_rate— emit custom shipping methods (e.g. zone-based or weight-tier rates).payment_customization— hide, rename, or reorder payment methods.delivery_customization— hide, rename, or reorder shipping options.order_validation— block order placement (e.g. min quantity, country block).discount— emit per-line or order-level discount entries.
Where each function fires
Functions execute at two hooks in the order pipeline:
- Cart validation — runs every time the cart is recalculated: cart page, checkout, address page. Five function types fire here:
cart_transform,shipping_rate,payment_customization,delivery_customization,discount. - Order placement — runs when the customer places an order.
order_validationfires here, and the cart-validation set re-runs as a final guard against client tampering.
The split matters: a cart_transform changes the visible cart instantly, while order_validation only blocks at the moment of order placement.
Anatomy of a function
Every function is a single function.js that exports a run(input) function returning an output object:
// cart_transform example: tag any line over $100 as "vip"
export function run(input) {
return {
operations: input.cart.lines
.filter(line => line.cost.totalAmount.amount > 100)
.map(line => ({
update: {
id: line.id,
properties: { tier: 'vip' },
},
})),
};
}
Alongside the JS you ship a manifest declaring the type, input/output schema version, and any merchant-configurable settings:
{
"name": "VIP tier tag",
"type": "cart_transform",
"handle": "vip-tier-tag",
"version": "1.0.0",
"input": { "schema": "cart.v1" },
"settings": [
{ "key": "threshold", "type": "number", "default": 100, "label": "Min line price ($)" }
]
}
How compilation works
The platform compiles your function.js using javy compile -d in dynamic mode. Dynamic mode links against a shared provider.wasm at runtime, which keeps your function module to roughly 3 KB. The hard size cap is 256 KB — plenty even for elaborate logic. Compilation happens server-side on upload. The compiled bytecode and the original source are stored alongside the app, while the app’s extension manifest stores just the metadata and a pointer to the compiled module.
Inputs by type
| Type | Input shape | Output shape |
|---|---|---|
cart_transform | { cart, settings } | { operations: [{ update | merge | split }] } |
shipping_rate | { cart, address, settings } | { rates: [{ name, price, code }] } |
payment_customization | { cart, paymentMethods, settings } | { operations: [{ hide | rename | move }] } |
delivery_customization | { cart, deliveryOptions, settings } | { operations: [{ hide | rename | move }] } |
order_validation | { cart, customer, settings } | { errors: [{ message, target }] } |
discount | { cart, settings } | { discounts: [{ targets, value, reason }] } |
Storefront consumption
The output of each function flows into a specific UI surface:
- cart_transform →
/cartpage renders the updated line items. - shipping_rate → checkout shipping picker appends
customShippingRates. - payment_customization → payment radios filter out hidden methods; renames flagged via
__renamedswap the label. - delivery_customization → shipping zones display renamed labels.
- order_validation → checkout displays the error and blocks the place-order button.
- discount → entries appear in the price breakdown on the order page as separate discount lines.
Testing your function
The portal includes a function test endpoint that runs your compiled function against a sample cart payload without going through real checkout:
POST /apps/functions/test
Content-Type: application/json
Authorization: Bearer YOUR_DEVELOPER_JWT
{
"appId": "...",
"handle": "vip-tier-tag",
"input": {
"cart": { "lines": [{ "id": "gid://line/1", "cost": { "totalAmount": { "amount": 150 } } }] },
"settings": { "threshold": 100 }
}
}
The response includes the function's output, execution time, and any console logs. Great for unit testing edge cases before publishing.
For end-to-end verification, install the app on a test store and drive the real checkout UI — that's the only way to confirm the UI consumers (price breakdown, payment radios, etc.) actually render the function's effect.
FAQ
How is a cart_transform different from a discount function?
cart_transform rewrites the line item itself — you can change price, properties, merge lines into bundles, split a single line. discount leaves the line untouched and emits a separate discount entry that shows up in the price breakdown. Use cart_transform for structural changes; use discount for "minus X dollars" with an audit trail.
Can one app ship multiple functions?
Yes. An app can ship any number of functions of any of the six types. Each function has its own handle and runs independently. Functions from the same app receive the same merchant settings namespace.
What language do I write functions in?
JavaScript today, compiled to portable bytecode by Javy. The dynamic-mode compile keeps modules tiny (~3 KB) by sharing a runtime provider; you focus on logic, not runtime weight.
Do functions have I/O?
No. Functions are pure: input in, output out. They can't read databases, call HTTP APIs, or read environment variables. If you need to fetch data, do it in your normal app backend and write the relevant facts into a metafield the function can read.
What's the timeout?
Functions run inline in cart validation and at order placement, so they must be fast — budget under 10 ms. The sandbox is single-threaded with no I/O, which makes hitting that easy in practice.
How do I roll back a buggy function?
Functions ship as part of an app version. Open the merchant's installed app row, click Rollback, and the previous version's functions are restored. As a developer you can also unpublish a version from the portal so no further merchants install it.