How it works
This section introduces you to "how JsRates works". Please read this section and watch the video at the end to understand how to use JsRates.
JsRates enables you to define custom shipping rules through JavaScript coding within the app's Editor page. Your main task is to develop a JavaScript module, named calculateShippingRates.js, which exports an asynchronous function calculateShippingRates().
This function is executed by the JsRates server to calculate shipping rates during customer checkout.

The calculateShippingRates() Function
Function signature: async function calculateShippingRates(DATA, env)
DATA: A JSON object that contains information about the shipping request, including the shipping origin, destination, cart items, currency, and locale. Shopify sends this data to your JsRates app whenever your customers proceed to checkout an order.env(optional): A Javascript object that contains encrypted environment variables that you defined in your JsRates app Settings page.
Your implementation of calculateShippingRates(DATA, env) should process the DATA object and return an array of shipping rates.
Sample DATA JSON object
"rate": {
"origin": {
"country": "CA",
"postal_code": "K2P1L4",
"province": "ON",
"city": "Ottawa",
"name": null,
"address1": "150 Elgin St.",
"address2": "",
"address3": null,
"phone": null,
"fax": null,
"email": null,
"address_type": null,
"company_name": "Jamie D's Emporium"
},
"destination": {
"country": "CA",
"postal_code": "K1M1M4",
"province": "ON",
"city": "Ottawa",
"name": "Bob Norman",
"address1": "24 Sussex Dr.",
"address2": "",
"address3": null,
"phone": null,
"fax": null,
"email": null,
"address_type": null,
"company_name": null
},
"items": [
{
"name": "Short Sleeve T-Shirt",
"sku": "",
"quantity": 1,
"grams": 1000,
"price": 1999,
"vendor": "Jamie D's Emporium",
"requires_shipping": true,
"taxable": true,
"fulfillment_service": "manual",
"properties": null,
"product_id": 48447225880,
"variant_id": 258644705304
}
],
"currency": "USD",
"locale": "en"
}
In the calculateShippingRates(DATA, env) function, you will use the information in the input DATA, such as, the destination country, destination city, destination postcode, the item sku, the item gram, the item quantity, or the item price, to setup rules for your shipping rates. Moreover, you can add additional product information fields in the items object, such as, metafields or tags, using JsRates built-in function enrichItemDetails (see the example below for more information about this function).
The function calculateShippingRates() must return shipping rates array that will be displayed to your customers during checkout.
Sample returned shipping rates array
{
"rates": [
{
"service_name": "canadapost-overnight",
"service_code": "ON",
"total_price": "1295",
"description": "This is the fastest option by far",
"currency": "CAD",
"min_delivery_date": "2013-04-12 14:48:45 -0400",
"max_delivery_date": "2013-04-12 14:48:45 -0400"
},
{
"service_name": "fedex-2dayground",
"service_code": "2D",
"total_price": "2934",
"currency": "USD",
"min_delivery_date": "2013-04-12 14:48:45 -0400",
"max_delivery_date": "2013-04-12 14:48:45 -0400"
},
{
"service_name": "fedex-priorityovernight",
"service_code": "1D",
"total_price": "3587",
"currency": "USD",
"min_delivery_date": "2013-04-12 14:48:45 -0400",
"max_delivery_date": "2013-04-12 14:48:45 -0400"
}
]
}
For a shipping rate request sent from your checkout page, the returned shipping rates array must contain the required fields: service_name, service_code,total_price, description, and currency. It can also include optional fields: phone_required, min_delivery_date, and max_delivery_date.
| Field | Description |
|---|---|
service_name |
The name of the rate, which customers see at checkout (e.g., Expedited Mail). |
description |
A description of the rate, which customers see at checkout (e.g., Includes tracking and insurance). |
service_code |
A unique code associated with the rate (e.g., expedited_mail). |
currency |
The currency of the shipping rate. |
total_price |
The total price expressed in subunits. If the currency doesn't use subunits, then the value must be multiplied by 100 (e.g., "total_price": 500 for 5.00 CAD, "total_price": 100000 for 1000 JPY). |
phone_required |
Whether the customer must provide a phone number at checkout. |
min_delivery_date |
The earliest delivery date for the displayed rate. |
max_delivery_date |
The latest delivery date for the displayed rate to still be valid. |
A sample code
The following sample code accepts an input DATA JSON object, calculates the shipping rate based on the total items price, and returns an object RATES containing a shipping rate array. The code sets up a shipping rule that returns a free shipping if the total cart items price is greater than or equal to $100; or else it returns a shipping fee of $10 if total cart items price is less than $100.
// Import helper function from JsRates tools module.
// enrichItemDetails() can add additional fields (e.g. tags, metafields) to DATA.items
// so you can build richer shipping logic without calling Shopify APIs manually.
import { enrichItemDetails } from "./modules.js";
/**
* Main entry point for JsRates.
*
* @param {Object} DATA - JSON payload sent by Shopify at checkout.
* Contains:
* - DATA.origin / DATA.destination: shipping origin and destination address
* - DATA.items: array of cart line items (product_id, variant_id, sku, grams, price, quantity, etc.)
* - DATA.currency: checkout currency (e.g. "USD", "AUD")
*
* @param {Object} env - Optional environment object.
* Use this to access your stored environment variables (API keys, credentials, etc.).
*/
export async function calculateShippingRates(DATA, env) {
try {
// Optionally enrich DATA.items with extra product and variant information.
// In this example, we ask JsRates to load up to 15 variant metafields
// from the "custom" namespace for each line item.
// You can change namespace/size to match your own metafield setup.
await enrichItemDetails(DATA, [{ namespace: "custom", size: 15 }]);
// --- Example business logic ---
// Calculate the cart total (in dollars) from item prices (in cents).
const totalPrice = DATA.items.reduce(
(sum, item) => sum + (item.price * item.quantity) / 100,
0
);
// Log the total price for debugging; visible in the "Print" section / logs.
console.log("total price", totalPrice);
// This array will hold all shipping options returned to Shopify.
const rates = [];
// Simple rule:
// - If total >= $100 → Free Shipping
// - Otherwise → $10 Standard Shipping
if (totalPrice >= 100) {
// Free shipping for orders over $100
rates.push({
service_name: "Free Shipping", // Label shown to customer
service_code: "FREE", // Internal code (you choose)
total_price: 0, // Price in cents → $0.00
description: "Free shipping for orders over $100",
currency: DATA.currency // Use checkout currency
});
} else {
rates.push({
service_name: "Standard Shipping",
service_code: "STD",
total_price: 1000, // 1000 cents = $10.00
description: "Standard shipping rate for all orders",
currency: DATA.currency
});
}
// JsRates expects an object with a "rates" array.
// Only this array is sent back to Shopify.
return { rates };
} catch (error) {
// Catch any unexpected errors during rate calculation
console.error("Error calculating shipping rates:", error.message);
// Optionally return a fallback rate or error marker.
// This example returns a single "error" rate that you can detect in logs
// or replace with a safer default (e.g. flat $X shipping).
return {
rates: [
{
service_name: "Shipping Rate Error",
service_code: "ERROR",
total_price: null, // No price returned
description: "Unable to calculate shipping rate",
currency: DATA.currency || ""
}
]
};
}
}
Running the sample code
Use the following steps to run the above sample code:
-
Copy the above sample code, paste it into a blank calculateShippingRates.js module in your JsRates app Editor page. Save the code by pressing the Save button or using the shortcut
Ctrl + S(Cmd + Son macOS) in the editor. -
Ensure that the test input DATA JSON is correctly saved. To do this:
- Click the More actions button and select Test data from the drop-down menu.
- In the pop-up window, you'll find fields for test item, test origin, test destination, test currency, and test locale. These fields are automatically populated during installation. Confirm that they are accurate, complete, and correct. You can modify them as needed for testing different configurations or adding more test items. Remember to save any changes before closing the pop-up window.
-
Run the test code by clicking the Run button in the More actions drop-down menu. The code will be parsed and executed on the JsRates backend. The results will be displayed in a new window at the bottom of the page. You can expand this window using the "expand" icon located at the top right corner.
-
Examine the resulting JSON object which includes the following fields:
duration: This represents the time in seconds taken to process the request. It provides an indication of how quickly your shipping rates will appear to customers during checkout. Note that Shopify ignores shipping rate requests that take longer than 10 seconds.rates: This array contains the shipping rates and will be returned to requests sent from your checkout page.DATA: This is the request DATA JSON object with theitemsarray enriched with additional fields that are fetched using theenrichItemDetailsfunction.print: This object contains the values of variables you want to debug. In this example, the variabletotalPriceis included. You can use theconsole.log()function to display the value of any variable within your code.
The following video demonstrates how to run the sample code.
