Cloud Functions

Offload compute intensive or security sensitive functions to the server.

Defining Cloud Functions

For complex apps, sometimes you just need a bit of logic that isn’t running on a mobile device. Cloud Code makes this possible.

Cloud Code is easy to use because it’s built on the same Moralis JavaScript SDK that powers thousands of apps. The only difference is that this code runs in your Moralis Server rather than running on the user’s mobile device. When you update your Cloud Code, it becomes available to all mobile environments instantly. You don’t have to wait for a new release of your application. This lets you change app behavior on the fly and add new features faster.

Even if you’re only familiar with mobile development, we hope you’ll find Cloud Code straightforward and easy to use.

Let’s look at a slightly more complex example where Cloud Code is useful. One reason to do computation in the cloud is so that you don’t have to send a huge list of objects down to a device if you only want a little bit of information. For example, let’s say you’re writing an app that lets people review movies. A single Review object could look like:

{
"movie": "The Matrix",
"stars": 5,
"comment": "Too bad they never made any sequels."
}

If you wanted to find the average number of stars for The Matrix, you could query for all of the reviews, and average the stars on the device. However, this uses a lot of bandwidth when you only need a single number. With Cloud Code, we can just pass up the name of the movie, and return the average star rating.

Cloud functions accept a JSON parameters dictionary on the request object, so we can use that to pass up the movie name. The entire Moralis JavaScript SDK is available in the cloud environment, so we can use that to query over Review objects. Together, the code to implement averageStars looks like:

Moralis.Cloud.define("averageStars", async (request) => {
const query = new Moralis.Query("Review");
query.equalTo("movie", request.params.movie);
const results = await query.find();
let sum = 0;
for (let i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
return sum / results.length;
});

The only difference between using averageStars and hello is that we have to provide the parameter that will be accessed in request.params.movie when we call the Cloud function. Read on to learn more about how Cloud functions can be called.

Global Packages

The following packages are available globally within Cloud Function code and can be used without a require statement.

Console.log

For debugging or informational purposes it's often useful to print messages. A logger can be obtained for this purpose. It will print messages to the Moralis Dashboard in the "Logs > Info" section.

const logger = Moralis.Cloud.getLogger();
logger.info("Hello World");

IDE Setup

You can write your cloud functions in your preferred IDE by making use of the moralis-admin-cli.

To get started you need to install it by running the following code in the terminal:

npm install -g moralis-admin-cli

After you have installed the Moralis Admin CLI, you can head the admin panel and open up the cloud functions on the server you want to work on.

In the lower part of the modal there will be a code snippet that you need to run in the terminal.

The only thing you need to change is the path to the local Javascript file on your computer that contains the cloud functions.

After you have run the command the cloud code will be updated automatically on the backend with each save!

Calling Cloud Functions

const params = { movie: "The Matrix" };
const ratings = await Moralis.Cloud.run("averageStars", params);
// ratings should be 4.5

In general, two arguments will be passed into cloud functions:

  1. request - The request object contains information about the request. The following fields are set:

  2. params - The parameters object sent to the function by the client.

  3. user - The Moralis.User that is making the request. This will not be set if there was no logged-in user.

If the function is successful, the response in the client looks like:

{ "result": 4.8 }

If there is an error, the response in the client looks like:

{
"code": 141,
"error": "movie lookup failed"
}

Using the Master Key in cloud code

Set useMasterKey:true in the requests that require master key.

Examples:

query.find({useMasterKey:true});
object.save(null,{useMasterKey:true});
Moralis.Object.saveAll(objects,{useMasterKey:true});

Implementing cloud function validation

It’s important to make sure the parameters required for a Cloud function are provided, and are in the necessary format. you can specify a validator function or object which will be called prior to your cloud function.

Let’s take a look at the averageStars example. If you wanted to make sure that request.params.movie is provided, and averageStars can only be called by logged in users, you could add a validator object to the function.

Moralis.Cloud.define("averageStars", async (request) => {
const query = new Moralis.Query("Review");
query.equalTo("movie", request.params.movie);
const results = await query.find();
let sum = 0;
for (let i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
return sum / results.length;
},{
fields : ['movie'],
requireUser: true
});

If the rules specified in the validator object aren’t met, the Cloud Function won’t run. This means that you can confidently build your function, knowing that request.params.movie is defined, as well as request.user.

More Advanced Validation

Often, not only is it important that request.params.movie is defined, but also that it’s the correct data type. You can do this by providing an Object to the fields parameter in the Validator.

Moralis.Cloud.define("averageStars", async (request) => {
const query = new Moralis.Query("Review");
query.equalTo("movie", request.params.movie);
const results = await query.find();
let sum = 0;
for (let i = 0; i < results.length; ++i) {
sum += results[i].get("stars");
}
return sum / results.length;
},{
fields : {
movie : {
required: true,
type: String,
options: val => {
return val.length < 20;
},
error: "Movie must be less than 20 characters"
}
},
requireUserKeys: {
accType : {
options: 'reviewer',
error: 'Only reviewers can get average stars'
}
}
});

This function will only run if:

  • request.params.movie is defined

  • request.params.movie is a String

  • request.params.movie is less than 20 characters

  • request.user is defined

  • request.user.get('accType') is defined

  • request.user.get('accType') is equal to ‘reviewer’

However, the requested user could set ‘accType’ to reviewer, and then recall the function. Here, you could provide validation on a Moralis.User beforeSave trigger. beforeSave validators have a few additional options available, to help you make sure your data is secure.

Moralis.Cloud.beforeSave(Moralis.User, () => {
// any additional beforeSave logic here
}, {
fields: {
accType: {
default: 'viewer',
constant: true
},
},
});

This means that the field accType on Moralis.User will be ‘viewer’ on signup, and will be unchangable, unless masterKey is provided.

The full range of built-in Validation Options are:

  • requireMaster: whether the function requires a masterKey to run.

  • requireUser: whether the function requires a request.user to run.

  • validateMasterKey: whether the validator should run on masterKey (defaults to false).

  • fields: an Array or Object of fields that are required on the request.

  • requireUserKeys: an Array of fields to be validated on request.user.

The full range of built-in Validation Options on .fields are:

  • type: the type of the request.params[field] or request.object.get(field).

  • default: what the field should default to if it’s null.

  • required: whether the field is required.

  • options: a singular option, array of options, or custom function of allowed values for the field.

  • constant: whether the field is immutable.

  • error: a custom error message if validation fails.

You can also pass a function to the Validator. This can help you apply reoccuring logic to your Cloud Code.

const validationRules = request => {
if (request.master) {
return;
}
if (!request.user || request.user.id !== 'masterUser') {
throw 'Unauthorized';
}
}
Moralis.Cloud.define('adminFunction', request => {
// do admin code here, confident that request.user.id is masterUser, or masterKey is provided
},validationRules)
Moralis.Cloud.define('adminFunctionTwo', request => {
// do admin code here, confident that request.user.id is masterUser, or masterKey is provided
},validationRules)

Some considerations to be aware of

  • The validation function will run prior to your Cloud Code Functions. You can use async and promises here, but try to keep the validation as simple and fast as possible so your cloud requests resolve quickly.

  • As previously mentioned, cloud validator objects will not validate if a master key is provided, unless validateMasterKey:true is set. However, if you set your validator to a function, the function will always run.

Calling via REST API

Cloud functions can be called directly using a simple GET request. Add your Moralis App Id and any Cloud Function arguments as query parameters to your Moralis server URL. Say you had the following Cloud Function.

Moralis.Cloud.define("Hello", (request) => {
return `Hello ${request.params.name}! Cloud functions are cool!`
});

Then the url would look something like the following:

https://1a2b3c4c5d6f.moralis.io:2053/server/functions/Hello?_ApplicationId=1a2b3c4c5d6f1a2b3c4c5d6f&name=CryptoChad

The URL has the following structure

  1. Full Morlis server url

  2. /functions/

  3. Cloud Function Name

  4. ?_ApplicationId=yourMoralisAppId

  5. (optional) Cloud Function param key/value pairs: &param1=value&param2=value

Web3

Web3 functions are available within Cloud Code including the ability to call contract methods. Moralis uses the Web3.js library.

// get a web3 instance for a specific chain
const web3 = Moralis.web3ByChain("0x1"); // mainnet

Ask for a web3 object by supplying the chainId for the blockchain you wish to connect to. The following is a list of the currently supported chains.

Note, the web3 instance returned by Moralis.web3ByChain() cannot sign transactions. There is a way to sign a transaction using a private key, but this NOT recommended for security reasons.

Chain Name

ChainId

Ethereum (Mainnet)

0x1

Ropsten (Ethereum Testnet)

0x3

Rinkeby

0x4

Goerli (Ethereum Testnet)

0x5

Kovan

0x2a

Binance Smart Chain (Mainnet)

0x38

Binance Smart Chain (Testnet)

0x61

Matic (Mainnet)

0x89

Mumbai (Matic Testnet)

0x13881

Local Dev (Ganache, Hardhat)

0x539

Contracts

Once you have a web3 instance you can use it to make contract calls by constructing a contract instance with the ABI and contract address.

const contract = new web3.eth.Contract(abi, address);

For convenience Moralis bundles the Openzepplin ABI for ERC20, ERC721, and ERC1155.

  • Moralis.Web3.abis.erc20

  • Moralis.Web3.abis.erc721

  • Moralis.Web3.abis.erc1155

Brining it all together...

const web3 = Moralis.web3ByChain("0x38"); // BSC
const abi = Moralis.Web3.abis.erc20;
const address = "0x...."
// create contract instance
const contract = new web3.eth.Contract(abi, address);
// get contract name
const name = await contract.methods
.name()
.call()
.catch(() => "");

For more details on the Web3.js contract interface see the web3.eth.Contract section of the Web3.js docs.

The web3 instance returned by Moralis.web3ByChain() cannot sign transactions. In the near future it may be possible to do this with custom plugins. For now, if you need to make on-chain contract interactions consider doing them on the front end or create a NodeJS backend where you have access to Truffle's HdWalletProvider.

Contract ABI

To find the ABI for a contract already published on the Ethereum mainnet you can look on Etherscan by searching for the contract address and looking in the "Contract" tab. There are similar block explorers for other chains where the ABI is published. For instance you can go to BscScan for Binance Smart Chain.

For your own contracts the ABI can be found in the build directory after compiling the contract

  • Truffle: /truffle/build/contracts/myContract.json

  • Hardhat: /artifacts/contracts/myContract.sol/myContract.json

Cloud Jobs

Sometimes you want to execute long running functions, and you don’t want to wait for the response. Cloud Jobs are meant for just that.

Define a Job

📋 Moralis.Cloud.job("myJob", (request) => {
// params: passed in the job call
// headers: from the request that triggered the job
// log: the Moralis Server logger passed in the request
// message: a function to update the status message of the job object
const { params, headers, log, message } = request;
message("I just started");
return doSomethingVeryLong(request);
});

Scheduling a Job

You can schedule a job in the Jobs section in the Dashboard by clicking "Schedule a job" in the top right corner.

Pick a description, the job to run and provide parameters if needed

When you have configured your job you confirm it by pressing "Schedule" in the bottom right corner.

HttpRequest

Moralis Server includes Moralis.Cloud.httpRequest. It allows you to send HTTP requests to any HTTP Server. This function takes an options object to configure the call.

A simple GET request would look like:

Moralis.Cloud.httpRequest({
url: 'https://www.awesomewebsite.com/'
}).then(function(httpResponse) {
// success
console.log(httpResponse.text);
},function(httpResponse) {
// error
console.error('Request failed with response code ' + httpResponse.status);
});

Moralis.Cloud.httpRequest returns a Promise that will be resolved on a successful http status code; otherwise the Promise will be rejected. In the above example, we use then() to handle both outcomes.

A GET request that specifies the port number would look like:

Moralis.Cloud.httpRequest({
url: 'https://www.awesomewebsite.com:8080/'
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

Valid port numbers are 80, 443, and all numbers from 1025 through 65535.

By default, Moralis.Cloud.httpRequest does not follow redirects caused by HTTP 3xx response codes, the followRedirects: true option can be used to change this.

Moralis.Cloud.httpRequest({
url: 'https://www.awesomewebsite.com/',
followRedirects: true
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

Query Parameters

You can specify query parameters to append to the end of the url by setting params on the options object. You can either pass a JSON object of key value pairs like:

Moralis.Cloud.httpRequest({
url: 'http://www.google.com/search',
params: {
q : 'Sean Plott'
}
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

or as a raw String like this:

Moralis.Cloud.httpRequest({
url: 'http://www.google.com/search',
params: 'q=Sean Plott'
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

Setting Headers

You can send HTTP Headers by setting the header attribute of the options object. Let’s say you want set the Content-Type of the request, you can do:

Moralis.Cloud.httpRequest({
url: 'http://www.example.com/',
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

Sending a POST Request

You can send a post request by setting the method attribute of the options object. The body of the POST can be set using the body. A simple example would be:

Moralis.Cloud.httpRequest({
method: 'POST',
url: 'http://www.example.com/create_post',
body: {
title: 'Vote for Pedro',
body: 'If you vote for Pedro, your wildest dreams will come true'
}
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

This will send a post to http://www.example.com/create_post with body that is the url form encoded body attribute. If you want the body to be JSON encoded, you can instead do:

Moralis.Cloud.httpRequest({
method: 'POST',
url: 'http://www.example.com/create_post',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: {
title: 'Vote for Pedro',
body: 'If you vote for Pedro, your wildest dreams will come true'
}
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

To ensure that your HTTP request body is encoded correctly, please always include the charset in your Content-Type header.

Following Redirects

By default, Moralis.Cloud.httpRequest does not follow redirects caused by HTTP 3xx response codes. You can use the followRedirects option to change this behavior to follow redirects:

Moralis.Cloud.httpRequest({
url: 'http://www.example.com/',
followRedirects: true
}).then(function(httpResponse) {
console.log(httpResponse.text);
}, function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
});

The RESPONSE object

The response object passed into the success and error will contain:

  • status - The HTTP Response status.

  • headers - The response headers

  • buffer - The raw byte representation of the response body.

  • text - The raw response body.

  • data - The parsed response, if Cloud Code knows how to parse the content-type that was sent.

  • cookies - The cookies sent by the server.

Webhooks

Webhooks allow you to write your server-side logic in your own environment with any tools you wish to use. This can be useful if you want to use a language other than JavaScript, host it yourself for improved testing capabilities, or if you require a specialized library or technology not available in the Cloud Functions. Webhooks are currently available for beforeSave, afterSave, beforeDelete, afterDelete, and Cloud functions. To specify a new webhook, you can use the Moralis Dashboard in the Webhooks section located under Core.

Note: At the current time, custom webhooks cannot be set for the special classes _User and _Installation.

Cloud Function Webhooks

A webhook request for a Cloud function will contain the following parameters:

  • master: True if the master key was used and false otherwise.

  • user: If set, this will contain the Moralis User who made the request, in our REST API format. This is not set if the master key is used.

  • installationId: If available, the installationId which made the request.

  • params: A JSON object containing the parameters passed to the function. For example: { "foo": "bar" }

  • functionName: The name of the Cloud function.

To respond to this request, send a JSON object with the key error or success set. In the case of success, send back any data your client will expect; or simply true if your client doesn’t require any data. In the case of error, the value provided should be the error message you want to return.

To create a webhook for a Cloud function, start by writing the function’s code on your own server.

You can activate a webhook from the Dashboard UI.

Once the webhook is set, you can call it from the Moralis SDK, the same way you would a normal Cloud function.

Webhooks are great when you want to use a specialized technology not available using Motalis Cloud Functions.

beforeConsume Webhooks

beforeConsume Webhooks are required when adding events that contain a large amount of data for example the Transfer(address,address,uint256) event, adding a new Sync and Watch Contract of this event without an address will synchronize millions of events per hour, making the instance unresponsive.

An example on how to use this Webhook would be to listen to all transfers event but only save to the DB if specific filters are fullfilled, like filtering than the value of the transfer is greater than X

You need to define the beforeConsume and set the tableName used on plugin Creation.

Moralis.Cloud.beforeConsume('TransfersEvent', function (object) {
if(object.value > 100000000000){
return true;
}
return false;
})

beforeSave Webhooks

For triggers, the following parameters are sent to your webhook.

  • master: True if the master key was used and false otherwise.

  • user: If set, this will contain the Moralis User who made the request, in our REST API format.

  • installationId: If available, the installationId which made the request.

  • object: For triggers, this will contain the Moralis Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.

  • triggerName: “beforeSave”

To respond to a beforeSave request, send a JSON object with the key error or success set. This is the same as for Cloud functions, but there’s an extra capability with beforeSave triggers. By returning an error, you will cancel the save request and the object will not be stored in Moralis. You can also return a JSON object in this following format to override the values that will be saved for the object:

📋{
"className": "AwesomeClass",
"existingColumn": "sneakyChange",
"newColumn": "sneakyAddition"
}

afterSave Webhooks

Like we’ve seen in normal Cloud Functions, it’s also possible to run some code after an object has been saved using a webhook. The parameters sent to your webhook are the same as for beforeSave triggers but we’ll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.

  • user: If set, this will contain the Moralis User who made the request, in our REST API format.

  • installationId: If available, the installationId which made the request.

  • object: For triggers, this will contain the Moralis Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.

  • triggerName: “afterSave”

No response is required for afterSave triggers.

beforeDelete Webhooks

You also use webhooks for beforeDelete triggers. The parameters sent to your webhook are the same as for beforeSave and afterSave triggers but we’ll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.

  • user: If set, this will contain the Moralis User who made the request, in our REST API format.

  • installationId: If available, the installationId which made the request.

  • object: For triggers, this will contain the Moralis Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.

  • triggerName: “beforeDelete”

Just like for Cloud functions, to respond to a beforeDelete request, send a JSON object with the key error or success set. Returning an error will cancel the delete and the object will remain in your database.

afterDelete Webhooks

The afterDelete trigger is also accessible via webhooks. The parameters sent to your webhook are the same as for other triggers but we’ll repeat them here for clarity.

  • master: True if the master key was used and false otherwise.

  • user: If set, this will contain the Moralis User who made the request, in our REST API format.

  • installationId: If available, the installationId which made the request.

  • object: For triggers, this will contain the Moralis Object, in our REST API format. For example: { "className": "TestObject", "foo": "bar" }.

  • triggerName: “afterDelete”

No response is required for afterDelete triggers.

Config

Moralis Config offers a convenient way to configure parameters in Cloud Code.

📋const config = await Moralis.Config.get({useMasterKey: true});
const privateParam = config.get("privateParam");

By default, Moralis Config parameters can be publicly read which may be undesired if the parameter contains sensitive information that should not be exposed to clients. A parameter can be made readable only with the master key by setting the Requires master key? property via the Moralis Dashboard to Yes.