Triggers
Run Code on the Server in Response to Server Events.
Introduction
Triggers are a way to perform operations in response to particular server-side events such as when data is saved to a particular collection. These typically come in two flavors.
Before X: trigger is called before X happens.
After X: trigger is called after X happens.
Triggers operate against a specific collection like EthTransactions
. For example, to update the status of an order object to "sold" when a particular NFT is transferred, you could define an afterSave
trigger on EthNFTTransfers
. The trigger would get called every time a new entry is added to the EthNFTTransfers
collection, and you could check to see if it matches one of the open orders.
Unconfirmed Transactions
Transactions on Testnet and Mainnet can take a while to be confirmed. When Moralis detects a new transaction (or event) in an unconfirmed state, these get put into the collection with confirmed: false
. When the transaction gets confirmed, the status is updated to confirmed: true
.
Consequences for Triggers
This means if you define an afterSave
trigger on a collection then the trigger can get fired TWICE- once for confirmed: false
and again for confirmed: true
! This can happen for any collection with a "confirmed" property. Write your trigger callback function with this behavior in mind. If the trigger creates entries in other collections then this could result in duplicate entries, or double counting in calculations, etc.
The confirmed status can be checked by getting the object that fired the trigger using request.object
.
Local Dev Chains
Ganache and Hardhat only process new blocks when a transaction is made. This means if you mint a new NFT or make a token transfer these transactions could get "stuck" in a pending table. These will get moved out of pending after one confirmation.
Tutorials
Legacy UI is present in the video, some things might be different (you can no longer write Cloud code in the UI)
Save Triggers
beforeSave
IMPLEMENTING DATA VALIDATION
Another reason to run code in the cloud is to enforce a particular data format. For example, you might have both an Android and an iOS app, and you want to validate data for each of those. Rather than writing code once for each client environment, you can write it just once with cloud code.
Data validation can be done in the code of the trigger.
If the function throws, then the Review
object will not be saved and the client will get an error. If nothing is thrown, the object will be saved normally.
One useful tip is that even if your app has many different versions, the same version of cloud code applies to all of them. Thus, if you launch an application that doesn’t correctly check the validity of input data, you can still fix this problem by adding a validation with beforeSave
.
MODIFYING OBJECTS ON SAVE
In some cases, you don’t want to throw out invalid data. You just want to tweak it a bit before saving it. beforeSave
can handle this case, too. Any adjustment you make to request.object will be saved.
In our movie review example, we might want to ensure that comments aren’t too long. A single long comment might be tricky to display. We can use beforeSave
to truncate the comment
field to 140 characters:
PREDEFINED CLASSES
If you want to use beforeSave
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
afterSave
In some cases, you may want to perform some type of action, such as a push, after an object has been saved. You can do this by registering a handler with the afterSave
method. For example, suppose you want to keep track of the number of comments on a blog post. You can do that by writing a function like this:
ASYNC BEHAVIOR
In the example above, the client will receive a successful response before the promise in the handler completes, regardless of how the promise resolves. For instance, the client will receive a successful response even if the handler throws an exception. Any errors that occurred while running the handler can be found in the cloud code log.
You can use an afterSave
handler to perform lengthy operations after sending a response back to the client. In order to respond to the client before the afterSave
handler completes, your handler may not return a promise and your afterSave
handler may not use async/await.
PREDEFINED CLASSES
If you want to use afterSave
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
Context
When saving a Moralis.Object
, you may pass a context
dictionary that's accessible in the Cloud Code Save Triggers. More info in the JavaScript Guide.
The context is also passed from a beforeSave
handler to an afterSave
handler. The following example sends emails to users who are being added to a Moralis.Role’s user relation asynchronously, so that the client receives a response before the emails complete sending:
Delete Triggers
beforeDelete
You can run custom cloud code before an object is deleted. You can do this with the beforeDelete
method. For instance, this can be used to implement a restricted delete policy that is more sophisticated than what can be expressed through ACLs. For example, suppose you have a photo album app, where many photos are associated with each album, and you want to prevent the user from deleting an album if it still has a photo in it. You can do that by writing a function like this:
If the function throws, the Album
object will not be deleted, and the client will get an error. Otherwise, the object will be deleted normally.
PREDEFINED CLASSES
If you want to use beforeDelete
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
afterDelete
In some cases, you may want to perform some type of action, such as a push, after an object has been deleted. You can do this by registering a handler with the afterDelete
method. For example, suppose that after deleting a blog post, you also want to delete all associated comments. You can do that by writing a function like this:
The afterDelete
handler can access the object that was deleted through request.object
. This object is fully fetched, but cannot be refetched or resaved.
The client will receive a successful response to the delete request after the handler terminates, regardless of how the afterDelete
terminates. For instance, the client will receive a successful response even if the handler throws an exception. Any errors that occurred while running the handler can be found in the cloud code log.
PREDEFINED CLASSES
If you want to use afterDelete
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
File Triggers
beforeSaveFile
With the beforeSaveFile
method, you can run custom cloud code before any file is saved. Returning a new Moralis.File
will save the new file instead of the one sent by the client.
EXAMPLES
METADATA AND TAGS
Adding Metadata and Tags to your files allows you to add additional bits of data to the files that are stored within your storage solution (i.e AWS S3). The beforeSaveFile
hook is a great place to set the metadata and/or tags on your files.
Note: not all storage adapters support metadata and tags. Check the documentation for the storage adapter you’re using for compatibility.
afterSaveFile
The afterSaveFile
method is a great way to keep track of all the files stored in your app. For example:
beforeDeleteFile
You can run custom cloud code before any file gets deleted. For example, let's say you want to add logic that only allows files to be deleted by the user who created it. You could use a combination of the afterSaveFile
and the beforeDeleteFile
methods as follows:
afterDeleteFile
In the above beforeDeleteFile
example, the FileObject
collection is used to keep track of saved files in your app. The afterDeleteFile
trigger is a good place to clean up these objects once a file has been successfully deleted.
Find Triggers
beforeFind
In some cases, you may want to transform an incoming query, adding an additional limit or increasing the default limit, adding extra includes, or restrict the results to a subset of keys. You can do so with the beforeFind
trigger.
EXAMPLES
PREDEFINED CLASSES
If you want to use beforeFind
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
afterFind
In some cases, you may want to manipulate the results of a query before they are sent to the client. You can do so with the afterFind
trigger.
PREDEFINED CLASSES
If you want to use afterFind
for a predefined class in the Moralis JavaScript SDK, you should not pass a string for the first argument. Instead, you should pass the class itself, for example:
Session Triggers
beforeLogin
Sometimes you may want to run custom validation on a login request. The beforeLogin
trigger can be used for blocking an account from logging in (for example, if they are banned), recording a login event for analytics, notifying a user by email if a login occurred at an unusual IP address and more.
SOME CONSIDERATIONS TO BE AWARE OF
It waits for any promises to resolve.
The user is not available on the request object - the user has not yet been provided a session until after
beforeLogin
is successfully completed.Like
afterSave
onMoralis.User
, it will not save mutations to the user unless explicitly saved.
THE TRIGGER WILL RUN…
On username & password logins.
On
authProvider
logins.
THE TRIGGER WON’T RUN…
On sign up.
If the login credentials are incorrect.
afterLogout
Sometimes you may want to run actions after a user logs out. For example, the afterLogout
trigger can be used for clean-up actions after a user logs out. The triggers contain the session object that has been deleted on logout. From this session object, you can determine the user who logged out to perform user-specific tasks.
SOME CONSIDERATIONS TO BE AWARE OF
As with with
afterDelete
triggers, the_Session
object that is contained in the request has already been deleted.
THE TRIGGER WILL RUN…
When the user logs out and a
_Session
object was deleted.
THE TRIGGER WON’T RUN…
If a user logs out and no
_Session
object was found to delete.If a
_Session
object is deleted without the user logging out by calling the logout method of an SDK.
LiveQuery Triggers
beforeConnect
You can run custom cloud code before a user attempts to connect to your LiveQuery server with the beforeConnect
method. For instance, this can be used to only allow users that have logged in to connect to the LiveQuery server.
In most cases, the connect
event is called the first time the client calls subscribe
. If this is your use case, you can listen for errors using this event.
beforeSubscribe
In some cases, you may want to transform the incoming subscription query. Examples include adding an additional limit, increasing the default limit, adding extra includes, or restricting the results to a subset of keys. You can do so with the beforeSubscribe
trigger.
afterLiveQueryEvent
In some cases, you may want to manipulate the results of a Live Query before they are sent to the client. You can do so with the afterLiveQueryEvent
trigger.
EXAMPLES
By default, MoralisLiveQuery does not perform queries that require additional database operations. This is to keep your Moralis Server as fast and efficient as possible. If you require this functionality, you can perform these in afterLiveQueryEvent
.
SOME CONSIDERATIONS TO BE AWARE OF
Live Query events won’t trigger until the
afterLiveQueryEvent
trigger has been completed. Make sure any functions inside the trigger are efficient and restrictive to prevent bottlenecks.
onLiveQueryEvent
Sometimes you may want to monitor Live Query Events to be used with a 3rd party such as "Datadog." The onLiveQueryEvent
trigger can log events triggered, number of clients connected, number of subscriptions and errors.
Events
connect
subscribe
unsubscribe
ws_connect
ws_disconnect
ws_disconnect_error
“connect” differs from “ws_connect”, the former means that the client completed the connect procedure as defined by Moralis Live Query protocol, where “ws_connect” just means that a new websocket was created.
Last updated