Cloud function is a sub-module of LeanEngine that allows you to run functions on the cloud in response to the requests made by clients. Before you continue, make sure you have read LeanEngine Overview.
When developing your application, you may need to write logic that:
Are shared by multiple platforms (like iOS, Android, and web) and you wish to write them only once.
Need to be modified frequently (like the sorting rules of a list) but you don't want to release a new version of client each time you make a change.
Demand high network traffic or computing power (like producing statistics on a huge table) which you don't want to run on clients.
Need to be triggered when certain events happen (hooking). For example, when a user deletes an account, you may wish to delete the entries in other tables that are related to this account as well.
Need to bypass certain restrictions set by ACL.
Need to be performed routinely. For example, you may wish to clean up inactive accounts every month.
With cloud function, you can deploy these types of logic written in any language (JavaScript, Python, PHP, or Java) on the cloud and have LeanEngine run them for you.
If you have no idea how to deploy your project to LeanEngine, take a look at LeanEngine Quick Start.
Other Languages
This guide uses PHP as an example, but LeanEngine supports many other languages as well. You can choose the one you are familiar with for development:
LeanEngine offers two environments for each app: production environment and staging environment. When calling cloud functions within LeanEngine instances using SDK, no matter explicitly or implicitly (by triggering hooks), the SDK uses the function defined in the same environment as the instance. For example, if beforeDelete hook is defined and an object is deleted with SDK in the staging environment, the beforeDelete hook in the staging environment will be triggered.
When calling cloud functions outside of LeanEngine instances using SDK, no matter explicitly or implicitly, X-LC-Prod is set to be 1 by default, which means that the production environment will be used. For historical reasons, there are some differences between each SDK:
For Node.js, PHP, and Java SDKs, the production environment will always be used by default.
For Python SDK, when debugging locally with lean-cli, the staging environment will be used if it exists. Otherwise, the production environment will be used.
For Java example projects java-war-getting-started and spring-boot-getting-started, when debugging locally with lean-cli, the staging environment will be used if it exists. Otherwise, the production environment will be used (same as Python SDK).
You can specify the environment being used with SDK:
Apps with only trial instances would only have production environments. Please do not attempt to switch to staging environments.
Cloud Functions
In this example, a simple cloud function named hello is defined in src/cloud.php. By doing so, clients running on all platforms will be able to call it and get the return value of it. The computing process of the function is done on the cloud side rather than on the client side, so there will be less burden on the clients.
Now let's look into a more complex example.
Imagine that you have an app that lets users review the movies they have watched. An object containing a single review of a movie may look like this:
{
"movie": "Despicable Me",
"stars": 5,
"comment": "These minions are so cute. I wish they are real and I can have them in my home!"
}
stars is the score given by the user, ranging from 1 to 5. If you want to obtain the average score of Despicable Me, one thing you can do is to have the client search for all the reviews of this movie and calculate the average score on the device. However, this requires all the reviews of this movie to be fetched to the client, which leads to unnecessary network traffic. With cloud function, you can simply have the client pass the name of the movie to the cloud and receive the calculated score only.
Cloud functions accept parameters in JSON objects which we can include the name of the movie in. All the methods defined in LeanStorage PHP SDK can be used on LeanEngine, so we can write the cloud function averageStars like this:
use \LeanCloud\Engine\Cloud;
use \LeanCloud\Query;
use \LeanCloud\CloudException;
Cloud::define("averageStars", function($params, $user) {
$query = new Query("Review");
$query->equalTo("movie", $params["movie"]);
try {
$reviews = $query->find();
} catch (CloudException $ex) {
// Log the error message if the query fails
error_log($ex->getMessage());
return 0;
}
$sum = 0;
forEach($reviews as $review) {
$sum += $review->get("stars");
}
if (count($reviews) > 0) {
return $sum / count($reviews);
} else {
return 0;
}
});
The PHP SDK offers the Cloud::start function which can be used to quickly initialize cloud function services. The example below shows the index.php of a LeanEngine project dedicated to cloud function services:
<?php
use \LeanCloud\Engine\Cloud;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../src/cloud.php'; // Contains the definitions of cloud functions
Cloud::start();
Note that Cloud::start is only applicable to projects dedicated to cloud functions.
If the project also uses web hosting features, please do not use Cloud::start.
Parameters and Return Values
The following parameters can be accessed within a cloud function:
$params: array: The parameters sent from the client.
$user: User: The user logged in at the client side (according to the header X-LC-Session).
$meta: array: Other information about the client, including $meta['remoteAddress'] which is the IP address of the client.
Calling Cloud Functions with SDK
You can call cloud functions with any LeanCloud SDK:
// Construct the dictionary to be passed to the cloud
NSDictionary *dicParameters = [NSDictionary dictionaryWithObject:@"Despicable Me"
forKey:@"movie"];
// Call averageStars with parameters
[AVCloud callFunctionInBackground:@"averageStars"
withParameters:dicParameters
block:^(id object, NSError *error) {
if(error == nil){
// Success; object is the returned value from the cloud function
} else {
// Error handling
}
}];
var paramsJson = {
movie: "Despicable Me"
};
AV.Cloud.run('averageStars', paramsJson).then(function (data) {
// Success; data is the returned value from the cloud function
}, function (err) {
// Error handling
});
from leancloud import cloud
cloud.run('averageStars', movie='Despicable Me')
use \LeanCloud\Engine\Cloud;
$params = array(
"movie" => "Despicable Me"
);
Cloud::run("averageStars", $params);
// Construct the parameters to be passed to the cloud
Map<String, String> dicParameters = new HashMap<String, String>();
dicParameters.put("movie", "Despicable Me");
// Call averageStars with parameters
AVCloud.callFunctionInBackground("averageStars", dicParameters).subscribe(new Observer<AVObject>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(AVObject avObject) {
// succeed.
}
@Override
public void onError(Throwable throwable) {
// failed
}
@Override
public void onComplete() {
}
});
// Java SDK also supports caching results returned by cloud function, similar to AVQuery.
// For example, the following calls will use cached results if available,
// and the cached results will expire in 30 seconds (30000 ms).
AVCloud.callFunctionWithCacheInBackground("averageStars", dicParameters, AVQuery.CachePolicy.CACHE_ELSE_NETWORK, 30000)
.subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable disposable) {}
@Override
public void onNext(Object object) {
// succeed.
}
@Override
public void onError(Throwable throwable) {
// failed.
}
@Override
public void onComplete() {}
});
try {
Map response = await LCCloud.run('averageStars', params: { 'movie': '夏洛特烦恼' });
// deal with results
} on LCException catch (e) {
// deal with exceptions
}
By doing so, a local function call will be triggered without initiating an HTTP request.
If you want to invoke a cloud function via HTTP, use the runRemote method instead:
try {
$token = User::getCurrentSessionToken(); // invoke with a specific `sessionToken` (optional)
$result = Cloud::runRemote("averageStars", array("movie" => "Despicable Me"), $token);
} catch (\Exception $ex) {
// Error handling
}
Calling Cloud Functions with RPC
By calling cloud functions with RPC, LeanEngine will automatically serialize the HTTP response body and the SDK will get the response in the format of LeanObject:
The client will receive { "code": 123, "error": "Custom error message." } from the function above.
Timeouts
The time limit for a cloud function to be processed is 15 seconds. If the cloud function does not make a response after this, HTTP error 503 will be triggered with the error message The request timed out on the server. An error log like WARNING: [pool www] child ... exited on signal 9 (SIGKILL) after ... seconds from start will be printed on the server side.
Hooking
A hook can be automatically triggered when certain events happen (like before or after saving or updating an object). Keep in mind that:
Importing data on the dashboard will not trigger any hooks.
Dead loops may be caused if hooks are not appropriately defined.
Hooks cannot be applied to _Installation table.
For hooks starting with before (including onLogin), if an exception occurs inside the function, the data operation will be terminated. Therefore, you can reject certain data operations by having functions throw an error. For hooks starting with after (including onVerified), such exception will not terminate the data operation because the operation is already completed before the function is executed.
graph LR
A((save)) -->D{object}
D-->E(new)
E-->|beforeSave|H{error?}
H-->N(No)
N-->B[create new object on the cloud]
B -->|afterSave|C((done))
H-->Y(Yes)
Y-->Z((interrupted))
D-->F(existing)
F-->|beforeUpdate|I{error?}
I-->Y
I-->V(No)
V-->G[update existing object on the cloud]
G-->|afterUpdate|C
graph LR
A((delete))-->|beforeDelete|H{error?}
H-->Y(Yes)
Y-->Z((interrupted))
H-->N(No)
N-->B[delete object on the cloud]
B -->|afterDelete|C((done))
To ensure that hooks are triggered internally by LeanStorage services, our SDK will verify the source of each request. If the verification fails, the error message Hook key check failed will be returned. If you see such error message when debugging locally, make sure you are using our command-line interface for debugging.
beforeSave
You can perform an operation before an object is saved, like data verification and pre-processing. For example, a comment on a movie may be too long to be displayed on the client and needs to be cut off to 140 characters:
Cloud::beforeSave("Review", function($review, $user) {
$comment = $review->get("comment");
if ($comment) {
if (strlen($comment) > 140) {
// Cut off and add '…' in the end
$review->set("comment", substr($comment, 0, 140) . "…");
}
} else {
// Return an error without saving the object
throw new FunctionError("No comment provided!", 101);
}
});
afterSave
You can perform an operation after an object is saved. For example, to update the total number of comments after a comment is created:
Cloud::afterSave("Comment", function($comment, $user) {
$query = new Query("Post");
$post = $query->get($comment->get("post")->getObjectId());
$post->increment("commentCount");
try {
$post->save();
} catch (CloudException $ex) {
throw new FunctionError("An error occurred while trying to save the post: " . $ex->getMessage());
}
});
Or, to add a new field from for each new user:
Cloud::afterSave("_User", function($userObj, $currentUser) {
$userObj->set("from", "LeanCloud");
try {
$userObj->save();
} catch (CloudException $ex) {
throw new FunctionError("An error occurred while trying to save the user: " . $ex->getMessage());
}
});
beforeUpdate
You can perform an operation before an object is updated. You will be able to know which fields are updated, or reject the data operation:
Cloud::beforeUpdate("Review", function($review, $user) {
// If comment is changed, check its length
if (in_array("comment", $review->updatedKeys) &&
strlen($review->get("comment")) > 140) {
throw new FunctionError("Your comment cannot be longer than 140 characters.");
}
});
Do not attempt to modify $review since changes made to it will not be saved to LeanStorage. If you want to reject the change, you can have the function throw an error.
The object passed into the function is a temporary object and may be different from the one saved to LeanStorage in the end (which may have atomic operations applied to).
afterUpdate
Dead loops may be triggered if this hook is not defined properly. This may lead to extra API calls or even extra bills. See Preventing Dead Loops for more details.
You can perform an operation after an object is updated. You will be able to know which fields are updated (same as beforeUpdate).
Cloud::afterUpdate("Review", function($review, $user) {
if (in_array("comment", $review->updatedKeys) &&
strlen($review->get("comment")) < 5) {
error_log(review.ObjectId . " seems like a spam: " . comment);
}
});
Preventing Dead Loops
You might be wondering why we can modify and save the post object in the afterUpdate hook without triggering this hook again. This is because LeanEngine automatically identifies and pre-processes all the LeanObject objects passed in by a hook to prevent the hook to be triggered again.
However, if the following situations happen, you still need to handle them by yourself:
You called fetch on the LeanObject objects passed in.
You constructed the LeanObject objects passed in by yourself with methods like LeanObject::create().
To prevent such objects from triggering certain hooks, you can call LeanObject->disableBeforeHook() or LeanObject->disableAfterHook() on them:
Cloud::afterUpdate("Post", function($post, $user) {
// Directly modifying and saving the object will not trigger afterUpdate
$post->set('foo', 'bar');
$post->save();
// Use disableAfterHook if you called fetch on the object
$post->fetch();
$post->disableAfterHook();
$post->set('foo', 'bar');
$post->save();
// Use disableAfterHook if you constructed the object by yourself
$post = LeanObject::create("Post", $post->getObjectId());
$post->disableAfterHook();
$post->save();
});
beforeDelete
You can perform an operation before an object is deleted. For example, before an album is deleted, check if there are any photos in it:
Cloud::beforeDelete("Album", function($album, $user) {
$query = new Query("Photo");
$query->equalTo("album", $album);
try {
$count = $query->count();
} catch (CloudException $ex) {
throw new FunctionError("An error occurred when getting photo count: {$ex->getMessage()}");
}
if ($count > 0) {
// The delete operation will be aborted
throw new FunctionError("Cannot delete an album if it still has photos in it.");
}
});
afterDelete
You can perform an operation after an object is deleted. For example, when an album is being deleted, instead of checking if there are any photos left, we directly delete all the photos in it:
You can perform an operation after a user's email or phone number is verified:
Cloud::onVerifed("sms", function($user, $meta) {
error_log("User {$user->getUsername()} is verified by SMS.");
});
The first parameter is the type of verification (sms for phone number and email for email).
Keep in mind that you don't have to update fields like emailVerified with this hook since the system automatically updates them.
onLogin
You can perform an operation before a user tries to log in. For example, to prevent blocked users from logging in:
Cloud::onLogin(function($user) {
error_log("User {$user->getUsername()} is trying to log in.");
if ($user->get("blocked")) {
// The user cannot log in if an error is thrown (error 401 will be returned)
throw new FunctionError("Forbidden");
}
// The user will be logged in if the function returns normally
});
You can also verify access_token of a third party account using this hook.
Suppose there is a platform called Example with the following authData:
Be aware that the code above is for demonstration only.
We omitted a lot of logic for brevity,
such as verifying token's expiration date, handling user login in other ways, and recovering from network errors.
You can perform an operation after a message is delivered to the cloud but before it is delivered to the receiver. For example, to filter out certain keywords from the message:
You can perform an operation if a message is delivered to the receiver but the receiver is offline. For example, to slice the first 32 characters of the message as the title for the push notification:
Cloud::define('_receiversOffline', function($params, $user) {
error_log('_receiversOffline start');
// content is the content of the message
$shortContent = $params["content"];
if (strlen($shortContent) > 32) {
$shortContent = substr($shortContent, 0, 32);
}
$json = array(
// Self-increase the number of unread messages
// Can be set to a fixed number
"badge" => "Increment",
"sound" => "default",
// Using development certificate
"_profile" => "dev",
"alert" => shortContent
);
$pushMessage = json_encode($json);
return array(
"pushMessage" => $pushMessage,
);
});
_messageSent
You can perform an operation after a message is delivered to the receiver. For example, to print out a log in LeanEngine:
You can perform an operation after the signature verification (if enabled) of creating a conversation is completed but before the conversation is created. For example, to print out a log in LeanEngine:
You can perform an operation after the signature verification (if enabled) of adding a member into a conversation is completed but before the member is added. This will be triggered both when the member proactively joins the conversation or is added by another member. Keep in mind that this hook will not be triggered when creating a conversation with clientIds. For example, to print out a log in LeanEngine:
You can perform an operation after the signature verification (if enabled) of removing a member from a conversation is completed but before the member is removed. This will not be triggered when the member proactively quits the conversation. For example, to print out a log in LeanEngine:
You can perform an operation before the name, custom attributes, or mention properties of a conversation (including notifications) are updated. For example, to print out a log in LeanEngine:
The client will receive Cloud Code validation failed. Error detail: { "code": 123, "message": "An error occurred." } as the response. You can retrieve the error message by slicing the string.
Timeouts for Hooks
The time limit for a hook to be processed is 3 seconds. If a hook is triggered by another cloud function (like a beforeSave or afterSave triggered by saving an object), the time limit of this hook will be further limited by the time remaining.
For example, if a beforeSave is triggered by a cloud function that has already taken 13 seconds, there will be only 2 seconds left for this hook. See Timeouts.
Scheduled Tasks
You can set up timers to schedule your cloud functions. For example, to clean up temporary data every night, to send push notifications to users every Monday, etc. The timer can be accurate to a second.
The time restrictions applied to ordinary cloud functions also apply to scheduled functions. See Timeouts.
If a timer triggers more than 30 400 (Bad Request) or 502 (Bad Gateway) errors within the past 24 hours, the system will disable it and send a email to you regarding the issue. The error log timerAction short-circuited and no fallback available will also be printed out in the web console.
After deploying your program to LeanEngine, go to your app's Dashboard > LeanEngine > Scheduled tasks and click on Create a timer to create a timer for a cloud function. For example, if we have a function named logTimer:
Cloud::define("logTimer", function($params, $user) {
error_log("This log is printed by logTimer.");
});
You can specify the times a task gets triggered using one of the following expressions:
CRON expression
Interval in seconds
Take CRON expression as an example.
To print logs at 8am every Monday, create a timer for the function logTimer using CRON expression and enter 0 0 8 ? * MON for it.
When creating a timer, two optional options are available:
Execute Parameters: the parameters passed to the cloud function in a JSON object
Retry Policy: retry or cancel the task when it fails due to a cloud function timeout
After the timer is created, the dashboard will display Last executed and Next execution time.
Last executed is the time and result of the last execution.
Clicking on the details button will reveal more information about the task:
status: the status of the task; could be success or failed
uniqueId: a unique ID for the task
finishedAt: the exact time when the task finished (for successful tasks only)
statusCode: the HTTP status returned by the cloud function (for successful tasks only)
result: the response body returned by the cloud function (for successful tasks only)
error: error message (for failed tasks only)
retryAt: the time when the cloud will try to rerun the task (for failed tasks only)
If you want to suspend a timer temporarily, say, for debug purpose, you can click on the Disable button in the Status column.
Correspondingly, clicking on the Enable button will reactivate a suspended task.
If you want to modify or delete a task, you can click on the Edit or Delete button in the Operation column.
Special characters can be used in the following ways:
Character
Meaning
Usage
*
All values
All the values a field can have. For example, to run a task on every minute, set <minutes> to be *.
?
Unspecified value
Can be used on at most one of the two fields that accept this value. For example, to run a task on the 10th of every month regardless of what day it is, set <day-of-month> to be 10 and <day-of-week> to be ?.
-
Scope
For example, setting <hours> to be 10-12 means 10am, 11am, and 12pm.
,
Splitting multiple values
For example, setting <day-of-week> to be MON,WED,FRI means Monday, Wednesday, and Friday.
/
Interval
For example, setting <seconds> to be */15 means every 15 seconds starting from the 0th, which are the 0th, 15th, 30th, and 45th seconds.
Fields are concatenated with spaces. Values like JAN-DEC and SUN-SAT are case-insensitive (MON is the same as mon).
To illustrate:
Expression
Explanation
0 */5 * * * ?
Run a task every 5 minutes.
10 */5 * * * ?
Run a task every 5 minutes and the time to run it is always the 10th second of a minute (like 10:00:10, 10:05:10, etc.).
0 30 10-13 ? * WED,FRI
Run a task at the 10:30am, 11:30am, 12:30am, and 1:30pm of every Wednesday and Friday.
0 */30 8-9 5,20 * ?
Run a task every 30 minutes between 8am and 10am (8:00am, 8:30am, 9:00am, and 9:30am) on the 5th and 20th of every month.
The time zone followed by CRON expressions is UTC+0.
Using Master Key
Since cloud functions are running on the server side, we can assume that requests made by these functions are trustable. Therefore, you can enable global Master Key for all these requests, which will have your program ignore permission settings of classes and those done by ACL so it can access data in the cloud without any restrictions. The code below enables Master Key for your program:
// Often in src/app.php
Client::useMasterKey(true);
PHP Cloud Function Guide
Cloud function is a sub-module of LeanEngine that allows you to run functions on the cloud in response to the requests made by clients. Before you continue, make sure you have read LeanEngine Overview.
When developing your application, you may need to write logic that:
With cloud function, you can deploy these types of logic written in any language (JavaScript, Python, PHP, or Java) on the cloud and have LeanEngine run them for you.
If you have no idea how to deploy your project to LeanEngine, take a look at LeanEngine Quick Start.
Other Languages
This guide uses PHP as an example, but LeanEngine supports many other languages as well. You can choose the one you are familiar with for development:
Switching Environments
LeanEngine offers two environments for each app: production environment and staging environment. When calling cloud functions within LeanEngine instances using SDK, no matter explicitly or implicitly (by triggering hooks), the SDK uses the function defined in the same environment as the instance. For example, if
beforeDelete
hook is defined and an object is deleted with SDK in the staging environment, thebeforeDelete
hook in the staging environment will be triggered.When calling cloud functions outside of LeanEngine instances using SDK, no matter explicitly or implicitly,
X-LC-Prod
is set to be1
by default, which means that the production environment will be used. For historical reasons, there are some differences between each SDK:You can specify the environment being used with SDK:
Apps with only trial instances would only have production environments. Please do not attempt to switch to staging environments.
Cloud Functions
In this example, a simple cloud function named
hello
is defined insrc/cloud.php
. By doing so, clients running on all platforms will be able to call it and get the return value of it. The computing process of the function is done on the cloud side rather than on the client side, so there will be less burden on the clients.Now let's look into a more complex example.
Imagine that you have an app that lets users review the movies they have watched. An object containing a single review of a movie may look like this:
stars
is the score given by the user, ranging from 1 to 5. If you want to obtain the average score of Despicable Me, one thing you can do is to have the client search for all the reviews of this movie and calculate the average score on the device. However, this requires all the reviews of this movie to be fetched to the client, which leads to unnecessary network traffic. With cloud function, you can simply have the client pass the name of the movie to the cloud and receive the calculated score only.Cloud functions accept parameters in JSON objects which we can include the name of the movie in. All the methods defined in LeanStorage PHP SDK can be used on LeanEngine, so we can write the cloud function
averageStars
like this:The PHP SDK offers the
Cloud::start
function which can be used to quickly initialize cloud function services. The example below shows theindex.php
of a LeanEngine project dedicated to cloud function services:Note that
Cloud::start
is only applicable to projects dedicated to cloud functions. If the project also uses web hosting features, please do not useCloud::start
.Parameters and Return Values
The following parameters can be accessed within a cloud function:
$params: array
: The parameters sent from the client.$user: User
: The user logged in at the client side (according to the headerX-LC-Session
).$meta: array
: Other information about the client, including$meta['remoteAddress']
which is the IP address of the client.Calling Cloud Functions with SDK
You can call cloud functions with any LeanCloud SDK:
Calling Cloud Functions with REST API
See LeanEngine REST API Guide.
Calling Cloud Functions on LeanEngine
You can call the cloud functions defined by
LeanCloudEngineCloud::define
withLeanCloudEngineCloud::run
:By doing so, a local function call will be triggered without initiating an HTTP request.
If you want to invoke a cloud function via HTTP, use the
runRemote
method instead:Calling Cloud Functions with RPC
By calling cloud functions with RPC, LeanEngine will automatically serialize the HTTP response body and the SDK will get the response in the format of
LeanObject
:Error Codes
You can customize error codes for cloud functions in accordance with HTTP status codes.
The client will receive
{ "code": 211, "error": "Could not find the user." }
from the function above.The client will receive
{ "code": 123, "error": "Custom error message." }
from the function above.Timeouts
The time limit for a cloud function to be processed is 15 seconds. If the cloud function does not make a response after this, HTTP error
503
will be triggered with the error messageThe request timed out on the server
. An error log likeWARNING: [pool www] child ... exited on signal 9 (SIGKILL) after ... seconds from start
will be printed on the server side.Hooking
A hook can be automatically triggered when certain events happen (like before or after saving or updating an object). Keep in mind that:
_Installation
table.For hooks starting with
before
(includingonLogin
), if an exception occurs inside the function, the data operation will be terminated. Therefore, you can reject certain data operations by having functions throw an error. For hooks starting withafter
(includingonVerified
), such exception will not terminate the data operation because the operation is already completed before the function is executed.To ensure that hooks are triggered internally by LeanStorage services, our SDK will verify the source of each request. If the verification fails, the error message
Hook key check failed
will be returned. If you see such error message when debugging locally, make sure you are using our command-line interface for debugging.beforeSave
You can perform an operation before an object is saved, like data verification and pre-processing. For example, a comment on a movie may be too long to be displayed on the client and needs to be cut off to 140 characters:
afterSave
You can perform an operation after an object is saved. For example, to update the total number of comments after a comment is created:
Or, to add a new field
from
for each new user:beforeUpdate
You can perform an operation before an object is updated. You will be able to know which fields are updated, or reject the data operation:
Do not attempt to modify
$review
since changes made to it will not be saved to LeanStorage. If you want to reject the change, you can have the function throw an error.The object passed into the function is a temporary object and may be different from the one saved to LeanStorage in the end (which may have atomic operations applied to).
afterUpdate
Dead loops may be triggered if this hook is not defined properly. This may lead to extra API calls or even extra bills. See Preventing Dead Loops for more details.
You can perform an operation after an object is updated. You will be able to know which fields are updated (same as
beforeUpdate
).Preventing Dead Loops
You might be wondering why we can modify and save the
post
object in theafterUpdate
hook without triggering this hook again. This is because LeanEngine automatically identifies and pre-processes all theLeanObject
objects passed in by a hook to prevent the hook to be triggered again.However, if the following situations happen, you still need to handle them by yourself:
fetch
on theLeanObject
objects passed in.LeanObject
objects passed in by yourself with methods likeLeanObject::create()
.To prevent such objects from triggering certain hooks, you can call
LeanObject->disableBeforeHook()
orLeanObject->disableAfterHook()
on them:beforeDelete
You can perform an operation before an object is deleted. For example, before an album is deleted, check if there are any photos in it:
afterDelete
You can perform an operation after an object is deleted. For example, when an album is being deleted, instead of checking if there are any photos left, we directly delete all the photos in it:
onVerified
You can perform an operation after a user's email or phone number is verified:
The first parameter is the type of verification (
sms
for phone number andemail
for email).Keep in mind that you don't have to update fields like
emailVerified
with this hook since the system automatically updates them.onLogin
You can perform an operation before a user tries to log in. For example, to prevent blocked users from logging in:
You can also verify
access_token
of a third party account using this hook.Suppose there is a platform called Example with the following
authData
:and the platform name is
example_platform
. We can verify itsaccess_token
using the method below:Be aware that the code above is for demonstration only. We omitted a lot of logic for brevity, such as verifying token's expiration date, handling user login in other ways, and recovering from network errors.
Hooks for LeanMessage
See LeanMessage Overview > LeanEngine Hooks for more instructions on the methods introduced below.
_messageReceived
You can perform an operation after a message is delivered to the cloud but before it is delivered to the receiver. For example, to filter out certain keywords from the message:
_receiversOffline
You can perform an operation if a message is delivered to the receiver but the receiver is offline. For example, to slice the first 32 characters of the message as the title for the push notification:
_messageSent
You can perform an operation after a message is delivered to the receiver. For example, to print out a log in LeanEngine:
_conversationStart
You can perform an operation after the signature verification (if enabled) of creating a conversation is completed but before the conversation is created. For example, to print out a log in LeanEngine:
_conversationStarted
You can perform an operation after a conversation is created. For example, to print out a log in LeanEngine:
_conversationAdd
You can perform an operation after the signature verification (if enabled) of adding a member into a conversation is completed but before the member is added. This will be triggered both when the member proactively joins the conversation or is added by another member. Keep in mind that this hook will not be triggered when creating a conversation with
clientId
s. For example, to print out a log in LeanEngine:_conversationRemove
You can perform an operation after the signature verification (if enabled) of removing a member from a conversation is completed but before the member is removed. This will not be triggered when the member proactively quits the conversation. For example, to print out a log in LeanEngine:
_conversationUpdate
You can perform an operation before the name, custom attributes, or mention properties of a conversation (including notifications) are updated. For example, to print out a log in LeanEngine:
Error Codes for Hooks
You can define error codes for hooks like
beforeSave
:The client will receive
Cloud Code validation failed. Error detail: { "code": 123, "message": "An error occurred." }
as the response. You can retrieve the error message by slicing the string.Timeouts for Hooks
The time limit for a hook to be processed is 3 seconds. If a hook is triggered by another cloud function (like a
beforeSave
orafterSave
triggered by saving an object), the time limit of this hook will be further limited by the time remaining.For example, if a
beforeSave
is triggered by a cloud function that has already taken 13 seconds, there will be only 2 seconds left for this hook. See Timeouts.Scheduled Tasks
You can set up timers to schedule your cloud functions. For example, to clean up temporary data every night, to send push notifications to users every Monday, etc. The timer can be accurate to a second.
The time restrictions applied to ordinary cloud functions also apply to scheduled functions. See Timeouts.
If a timer triggers more than 30
400
(Bad Request) or502
(Bad Gateway) errors within the past 24 hours, the system will disable it and send a email to you regarding the issue. The error logtimerAction short-circuited and no fallback available
will also be printed out in the web console.After deploying your program to LeanEngine, go to your app's Dashboard > LeanEngine > Scheduled tasks and click on Create a timer to create a timer for a cloud function. For example, if we have a function named
logTimer
:You can specify the times a task gets triggered using one of the following expressions:
Take CRON expression as an example. To print logs at 8am every Monday, create a timer for the function
logTimer
using CRON expression and enter0 0 8 ? * MON
for it.When creating a timer, two optional options are available:
After the timer is created, the dashboard will display Last executed and Next execution time. Last executed is the time and result of the last execution. Clicking on the details button will reveal more information about the task:
status
: the status of the task; could besuccess
orfailed
uniqueId
: a unique ID for the taskfinishedAt
: the exact time when the task finished (for successful tasks only)statusCode
: the HTTP status returned by the cloud function (for successful tasks only)result
: the response body returned by the cloud function (for successful tasks only)error
: error message (for failed tasks only)retryAt
: the time when the cloud will try to rerun the task (for failed tasks only)You can see the logs of the timer in Dashboard > LeanEngine > App logs. For example:
If you want to suspend a timer temporarily, say, for debug purpose, you can click on the Disable button in the Status column. Correspondingly, clicking on the Enable button will reactivate a suspended task. If you want to modify or delete a task, you can click on the Edit or Delete button in the Operation column.
CRON Expressions
The basic syntax of a CRON expression is:
, - * /
, - * /
, - * /
, - * ? /
, - * /
, - ? /
Special characters can be used in the following ways:
*
<minutes>
to be*
.?
<day-of-month>
to be10
and<day-of-week>
to be?
.-
<hours>
to be10-12
means 10am, 11am, and 12pm.,
<day-of-week>
to beMON,WED,FRI
means Monday, Wednesday, and Friday./
<seconds>
to be*/15
means every 15 seconds starting from the 0th, which are the 0th, 15th, 30th, and 45th seconds.Fields are concatenated with spaces. Values like
JAN-DEC
andSUN-SAT
are case-insensitive (MON
is the same asmon
).To illustrate:
0 */5 * * * ?
10 */5 * * * ?
0 30 10-13 ? * WED,FRI
0 */30 8-9 5,20 * ?
The time zone followed by CRON expressions is
UTC+0
.Using Master Key
Since cloud functions are running on the server side, we can assume that requests made by these functions are trustable. Therefore, you can enable global Master Key for all these requests, which will have your program ignore permission settings of classes and those done by ACL so it can access data in the cloud without any restrictions. The code below enables Master Key for your program:
See ACL Guide and Using ACL in LeanEngine for more information regarding permission settings with LeanEngine.