Triggers
A Spin trigger maps an event, such as an HTTP request or a Redis pub-sub message, to a component that handles that event.
An application can contain multiple triggers.
In Spin 2.2 and earlier, all triggers must be of the same type. For example, an application can contain triggers for multiple HTTP routes, or for multiple Redis pub-sub channels, but not both.
In Spin 2.3 and later, an application can contain triggers of different types. For example, a single application can serve HTTP on one or more routes, and at the same time subscribe to one or more Redis pub-sub channels.
If you’re familiar with Spin 1.x, note that Spin 2 uses the term “trigger” to refer to each individual route or channel, rather than the trigger type. It’s closer to the
[component.trigger]
usage than to the application trigger.
Triggers and Components
How events are specified depends on the type of trigger involved. For example, an HTTP trigger is specified by the route it handles. A Cron Trigger is specified by the schedule on which it runs. A Redis trigger is specified by the channel it monitors. A trigger always, however, has a component
field, specifying the component that handles matching events. The component
can be specified in two ways.
Mapping a Trigger to a Named Component
An application manifest can define named components in the component
section. Each component is a WebAssembly component file (or reference) plus the supporting resources it needs, and metadata such as build information. The component name is written as part of the TOML component
declaration. For example:
[component.checkout] # The component's name is "checkout"
source = "target/wasm32-wasi/release/checkout.wasm"
allowed_outbound_hosts = ["https://payment-processing.example.com"]
key_value_stores = ["default"]
[component.checkout.build]
command = "cargo build --target wasm32-wasi --release"
To map a trigger to a named component, specify its name in the trigger’s component
field:
[[trigger.http]]
route = "/cart/checkout"
component = "checkout"
Writing the Component Inside the Trigger
Instead of writing the component in a separate section and referencing it by name, you can write it the same fields inline in the trigger component
field. For example:
# Using inline table syntax
[[trigger.http]]
route = "/cart/..."
component = { source = "dist/cart.wasm" }
# Nested table syntax
[[trigger.http]]
route = "/cart/..."
[trigger.http.component]
source = "target/wasm32-wasi/release/checkout.wasm"
allowed_outbound_hosts = ["payment-processing.example.com"]
These behave in the same way: the inline table syntax is more compact for short declarations, but the nested table syntax is easier to read when there are many fields or the values are long.
Choosing Between the Approaches
These ways of writing components achieve the same thing, so which should you choose?
Named components have the following advantages:
- Reuse. If you want two triggers to behave in the same way, they can refer to the same named component. Remember this means they are not just handled by the same Wasm file, but with the same settings.
- Named. If an error occurs, Spin can tell you the name of the component where the error happened. With inline components, Spin has to synthesize a name. This isn’t a big deal in single-component apps, but makes diagnostics harder in larger apps.
Inline components have the following advantages:
- Compact, especially when using inline table syntax.
- One place to look. Both the trigger event and the handling details are always in the same piece of TOML.
If you are not sure, or are not experienced, we recommend using named components at first, and adopting inline components as and when you find cases where you prefer them.
Setting Up Multiple Trigger Types
In this section, we build an application that contains multiple triggers.
Here is an example of creating an application with both HTTP and Redis triggers:
# Start with an empty application
$ spin new -t http-empty multiple-trigger-example
Description: An application that handles both HTTP requests and Redis messages
# Change into to the application directory
$ cd multiple-trigger-example
# Add an HTTP trigger application
$ spin add -t http-rust rust-http-trigger-example
Description: A Rust HTTP example
HTTP path: /...
# Add a Redis trigger application
$ spin add -t redis-rust rust-redis-trigger-example
Description: A Rust redis example
Redis address: redis://localhost:6379
Redis channel: one
The above spin new
and spin add
commands will scaffold a Spin manifest (spin.toml
file) with the following triggers:
spin_manifest_version = 2
[application]
name = "multiple-trigger-example"
version = "0.1.0"
authors = ["Your Name <your-name@example.com>"]
description = "An application that handles both HTTP requests and Redis messages"
[[trigger.http]]
route = "/..."
component = "rust-http-trigger-example"
[component.rust-http-trigger-example]
source = "rust-http-trigger-example/target/wasm32-wasi/release/rust_http_trigger_example.wasm"
allowed_outbound_hosts = []
[component.rust-http-trigger-example.build]
command = "cargo build --target wasm32-wasi --release"
workdir = "rust-http-trigger-example"
watch = ["src/**/*.rs", "Cargo.toml"]
[application.trigger.redis]
address = "redis://localhost:6379"
[[trigger.redis]]
channel = "one"
component = "rust-redis-trigger-example"
[component.rust-redis-trigger-example]
source = "rust-redis-trigger-example/target/wasm32-wasi/release/rust_redis_trigger_example.wasm"
allowed_outbound_hosts = []
[component.rust-redis-trigger-example.build]
command = "cargo build --target wasm32-wasi --release"
workdir = "rust-redis-trigger-example"
watch = ["src/**/*.rs", "Cargo.toml"]
Cron Trigger
Spin has experimental support for creating and running components on a schedule. Please note that there are only working Cron Trigger app samples written in Rust and Python at present.
Please note: You can not
spin deploy
an application to Fermyon Cloud if it usescron
because non-HTTP triggers are not supported in Fermyon Cloud.
Let’s look at how the experimental Cron trigger for Spin allows you to deploy an application that runs on a schedule. A Cron trigger maps a cron expression (a schedule) to a specific component. For example:
[[trigger.cron]]
component = "hello-cron"
cron_expression = "1/2 * * * * *"
Note: The 7th field (year) for the
cron_expression
is optional.
Cron Trigger Expressions
The expression is based on the crontab (cron table) syntax whereby each line is made up of 7 fields that represent the time to execute.
# ┌──────────── sec (0–59)
# | ┌───────────── min (0–59)
# | │ ┌───────────── hour (0–23)
# | │ │ ┌───────────── day of month (1–31)
# | │ │ │ ┌───────────── month (1–12)
# | │ │ │ │ ┌───────────── day of week (0–6)
# | │ │ │ │ | ┌─────────────- year
# | │ │ │ │ | │
# | │ │ │ │ | │
1/30 * * * * * *
For more information about setting the schedule, please see the Spin Cron Trigger repository.
Let’s look at a time-based workload inside a Rust application.
Installing the Cron Trigger Plugin
First, we install the plugin:
spin plugins install --url https://github.com/fermyon/spin-trigger-cron/releases/download/canary/trigger-cron.json
Installing the Cron Trigger Template
Then, we install the template:
spin templates install --git https://github.com/fermyon/spin-trigger-cron
Creating the Application
With the plugin and template installed, we create a new application:
spin new -t cron-rust hello_cron --accept-defaults
Inspecting the Source Code
The Rust source code for this application is as follows:
use spin_cron_sdk::{cron_component, Error, Metadata};
use spin_sdk::variables;
#[cron_component]
async fn handle_cron_event(metadata: Metadata) -> Result<(), Error> {
let key = variables::get("something").unwrap_or_default();
println!(
"[{}] Hello this is me running every {}",
metadata.timestamp, key
);
Ok(())
}
Building and Running the Application
We can immediately run this pre-written (template) application and observe the time-driven execution:
cd hello_cron
spin build --up
Building component hello-cron with `cargo build --target wasm32-wasi --release`
...
Finished building all Spin components
[1715640447] Hello from a cron component
[1715640449] Hello from a cron component
[1715640451] Hello from a cron component
[1715640453] Hello from a cron component
[1715640455] Hello from a cron component
[1715640457] Hello from a cron component
As we can see from the above output, our application is now running and executing the function every two seconds without the need for any incoming requests or any intervention from users or other machines.
If you would like to learn more about using the Spin Cron Trigger, please check out the Spin Cron Trigger blog post and the Spin Cron Trigger GitHub repository.