Deploy and Interact with Serverless Functions using EXM’s SDK
Written by Rohit Pathare
Execution Machine (EXM) is a developer platform that provides the ability to create and leverage blockchain-based (permanent) serverless functions without the need for knowledge of or access to blockchain technologies like wallets and tokens.
In an earlier article we understood how Execution Machine and its supporting technologies work.
In this article we will walkthrough a basic code example for deploying and interacting with serverless functions using EXM’s JavaScript SDK.
Basic Setup
We start off by creating an empty repository and install the Execution Machine package.
npm init -y
npm i @execution-machine/sdk
Defining the Function
After installing the package we create a file named function.js
in our repository and write the logic of our function within it.
export function handle(state, action) {
state.users.push(action.input.name);
return { state }
}
The syntax for defining functions is based off of the standard implemented by SmartWeave for smart contracts in JavaScript. Every function has a state
which is a JSON object of values stored in it and actions
to interact with these values.
In our function, we want to add names to a users array which is done using the following line:
state.users.push(action.input.name);
When deploying our function we initialise an empty array named users
that later helps our function to identify this state variable (variable stored in state of the function) during read and write calls. Upon initialisation the state
looks like this:
{ users: [] }
Additionally, while writing to the function, we use a key named name
to help the function identify what value we are feeding into the write operation. Both these definitions gain further significance when dealing with multiple values.
Handling the EXM API Token Safely
In order to use with EXM, we need a Token or Key which can be created here.
⚠️ The
API_TOKEN
is an identifier to our account and lets us access functions associated with it. Hence, it is vital to ensure this token is kept secret to prevent any spams and attacks to our functions. The best way to do so is using environment variables.
For setting up environment variables we install the dotenv
package and create a file named .env
with our API TOKEN as follows:
EXM_API_TOKEN='EXM_API_KEY_HERE'
To ensure these variables are not pushed to a github repository, add the following to a new file named .gitignore
:
.env
Deploying our Serverless Function
Once we are setup to interact with EXM safely, we need a file named deploy.js
to deploy our function on Arweave.
import { Exm, ContractType } from '@execution-machine/sdk';
import { readFileSync } from 'fs';
import dotenv from "dotenv";
dotenv.config();
// init new EXM instance
const exm = new Exm({ token: process.env.EXM_API_TOKEN });
// fetch function source
const functionSource = readFileSync('function.js');
// .deploy(source, initState, contractType)
const functionInst = await exm.functions.deploy(functionSource, { users: [] }, ContractType.JS);
console.log(functionInst);
For deploying our function, we need to pass in the function definition, initial state of function and function definition type as arguments. While deploying, we have stored in a variable and console logged the transaction as we are returned a functionId
that is needed for further interacting with our serverless function for read and write operations.
We are essentially creating a script here that tells our computer how to deploy our function to the network. To run this script we simply run the following command in our terminal and our function will be deployed:
node deploy.js
In return we get the following object logged in the console:
{ id: 'UNIQUE_FUNCTION_ID' }
Writing to the Function
With the function deployed we can finally interact with it. To start off let’s add a value to the users array using a new file write.js
.
import { Exm } from '@execution-machine/sdk';
import dotenv from "dotenv";
dotenv.config();
const exm = new Exm({ token: process.env.EXM_API_TOKEN });
const functionId = 'UNIQUE_FUNCTION_ID';
const inputs = [{ name: 'Open Sourcerer' }];
const writeTx = await exm.functions.write(functionId, inputs);
console.log(writeTx);
Our write transaction takes in two arguments. The functionId
of the function we want to interact with and any inputs
the function needs to process our request and update state.
We store the write transaction and log it to check the status and transaction Id. This is another script and can be run as follows:
node write.js
A successful write request returns an object with the status as SUCCESS
.
{
status: 'SUCCESS',
data: {
pseudoId: 'txnId',
execution: {
state: [Object],
result: null,
validity: [Object],
exmContext: [Object],
updated: false
}
}
}
Reading values from the Function
We can read values from the users array using a read function defined as follows in read.js
:
import { Exm } from '@execution-machine/sdk';
import dotenv from "dotenv";
dotenv.config();
const exm = new Exm({ token: process.env.EXM_API_TOKEN });
const functionId = 'UNIQUE_FUNCTION_ID';
const readTx = await exm.functions.read(functionId);
console.log(readTx);
This logs an object with the values we have written to the users array:
{ users: [ 'Open Sourcerer' ] }
Great job! If you need any help with the code, refer to the repository here.
Let’s Recap
Let us quickly recap what we did in this article.
We started off by creating an empty repository and installing the necessary. Then we wrote the logic for our function. For interacting with EXM we learnt to handle API keys safely. Once setup, we deployed our function and performed both write and read operations on it.
To learn more about EXM read their docs. For any help join the Discord or reach out on Twitter.