Implementing a WebAssembly Shopify Function using C++

Elad Kishon
6 min readJul 25, 2022

--

Part of my work @Rise.ai is to extend our customers' checkout experience with Rise.ai features

Quick intro

What are Shopify functions? From their overview:

Shopify Functions allow developers to customize the backend logic that powers parts of Shopify. The first place where we will allow you to extend logic is the discounting logic that lives inside of Checkout. Over time you will be able to manipulate the backend of many parts of Shopify.

As you can see Shopify wants developers to be able to write code that will be fast and secure enough to be integrated into their backend code in a first-citizen-like manner. This is an important requirement since a significant amount of apps can be added to a merchant store, and all of them will be run in very critical processes like checkout. Shopify is trying to minimize the impact of that on the customer experience in the store.

How do they go about doing that? leveraging WebAssembly, a new standard for the web and outside of it, to run code written by any language on any operating system, at presumably native speed. You can read more about how they did that in their blog on how they used WebAssembly outside of the browser.

In this post I’m going to show you a simple Discount example that Shopify already showed here, explain a little about how it works and how we used C++ rather than the built-in example in Rust, since its easier to onboard less experienced developers, especially those that are not familiar with Rust.

Let’s briefly explain how discounting logic works as a Shopify Function assuming our function is called Loyalty.

  • Create a discount type through Shopify Admin API with function_id
    equals to your function UUID.
  • Create a discount of type Loyalty called say, Apply Loyalty Discount.
  • Now whenever the state of the cart or checkout changes Shopify will run our function which returns one of two possible results FixedAmount or Percentage that will result in a discount being applied.

Starting with boilerplate

To implement that, we will create an app containing an extension (Shopify Function is an app extension) and install it on our development store.

For this example, we used Shopify CLI to generate an app boilerplate.

yarn init @shopify/app@latest

This generates in web/ a react web app with the intro and example page as embedded UI in the Shopify Admin dashboard. Since our app already exists, we only want to implement the Shopify Function so we don't need the code in /web , we’ll only use it to demonstrate our Shopify Function implementation by creating a page that creates the Discount.

Now we need to create a function extension we’ll do that by:

yarn scaffold extension

We’ll choose product discount in the menu, and type in the name Loyalty.
Now we can use Shopify’s Rust built-in boilerplate which will work perfectly, BUT I wanted to try implementing the same process using something I’m more familiar with like C++, and of course other team members as well.
So instead of selecting Rust in the menu choose wasm which will generate only the bare minimum files that you can build upon to create your wasm module.

The structure of a Shopify Function module

In the directory of your module these mandatory basic files are created by the CLI:

shopify.function.extension.toml — metafile containing all the information the CLI needs to compile and upload your function to Shopify.

name = "loyalty"type = "product_discounts"api_version = "2022-07"[build]command = "echo 'build the wasm'"
path = ''
[ui.paths]create = "/"details = "/"

The name will be the extension’s name and will show up as the Discount Type name as well. You’ll have to specify how to build this module and where the target compiled .wasm file is located with the command and path variables.

In the UI paths section, you’ll specify to which page in your app should the merchant will be redirected to create a Loyalty type Discount. You don’t necessarily need that if the app creates the discount for the merchant behind the scenes, and does not need a UI to manually create it.

input.graphql — contains the GraphQL query that’ll be executed for you and its result will be provided as STDIN input to your function.

Using C++ to output a compatible wasm target

A function is literally a function that takes the input.graphql result as stringified JSON into STDIN of our program and outputs a stringified JSON that conforms to the structure of FunctionResult. You can learn more about the input/output models here.

So to sum up what we need to do to have a working function is:

  • header file containing all Shopify-specific models
  • Ability to parse and stringify JSON easily
  • Compile our module to a WASM format

Let's get to work!

Shopify models

Ok so need to have all the Shopify models as a nice .h header file, here it is Thank me later :)

Input/Output

To parse and stringify JSON I found a nice MACRO library for C++ that works well called json_struct.h , you can download it here
You can then define structs as models normally and then add JSON encode/decode support easily like this:

Compiling

So it needs to be compiled into a standalone WASI compatible .wasm binary file.
Why? that’s how the Shopify engine works and it makes sense it runs the Wasm modules outside of the browser.
You can read more about WASI and why you need it here but in short,

WASI stands for WebAssembly System Interface. It’s an API designed by the Wasmtime project that provides access to several operating-system-like features, including files and filesystems, Berkeley sockets, clocks, and random numbers, that we’ll be proposing for standardization.

It’s designed to be independent of browsers, so it doesn’t depend on Web APIs or JS, and isn’t limited by the need to be compatible with JS. And it has integrated capability-based security, so it extends WebAssembly’s characteristic sandboxing to include I/O.

So we’ll use the WASI C/C++ toolchain to compile our CPP files into a Shopify-compatible wasm.

Requirements

  • Download the WASI-SDK for your operating system
  • Copy the contents of the extracted SDK to say: /usr/local/wasi-sdk-16.0

Now you can compile your .cpp source code to the wasm target

$ usr/local/wasi-sdk-16 src/main.cpp -o build/main.wasm

Let's look at the simple main.cpp that can be used as a skeleton to any function that you’ll want to create

You now obviously need to implement function but that’s just about it in terms of actually writing a function.

We now to add this compilation command and the wasm file path in shopify.function.extension.toml and hit yarn deploy .

But do you think that's it?

This by itself will probably not work like that out of the box, we still need a little tuning and explaining a few caveats for this to actually work with Shopify.

Here is a little test script I created you can see the function works at least insanity level while developing.
(The script reads an example file input.example.json removes new lines to mimic Shopify input and pipes it to the compiled program run by wasmtime)

As you can see, there are many flags that I’ve added to the compilation command that are quite essential for the code to be uploaded successfully to Shopify.

File size
It's not documented anywhere but I guess Shopify has an upload limit for the compiled wasm file that’s why I added -Oz flag for the compiler to optimize and consequently drop the file size but almost half.

Exceptions
Exceptions are not yet supported in Wasm runtime, so your try/catch clauses will just be dropped by the compiler and your code could crash unexpectedly in a random part of your code, which can be very annoying to debug.
I’ve added the -fno-exceptions flag for the compilation to fail if you add classic cpp error handling.

Cpp new features

You should add -std=c++17 to support the new c++ features like std::optional or std::variant which are used by api.h I’ve written.

To sum up

We’ve seen how Shopify Function can be used as a way to modify the checkout logic and in this specific example the discounting logic. In addition, it was fun to make C++ work the same way after all the little quirks we had to resolve in order to make it work properly in Shopify.

For the full source code of this example - https://github.com/eladkishon/shopify-functions-cpp-example

Thank you if you’ve read it so far! If you have any more questions feel free to reach out here or at kishonelad@gmail.com

--

--

Elad Kishon

Second engineer and team lead at Rise.ai (acquired by Wix.com) | alumni of top Israeli intelligence technology unit | open source advocate.