(Oct. 3rd 2024) Flawless is now in Public Beta. Try it out!
  Flawless Logo, a beautiful woman with freckles head illustration. flawless.dev docs discord

Your First Workflow

First, you will need to start the Flawless server with:

$ flawless up

You can visit the server dashboard at http://localhost:27288.

Screenshot of the flawless dashboard

Screenshot of the flawless server dashboard

Create a flawless module

The next step will be to create a flawless module. Modules are just a collection of workflows with a name and version. In fact, modules are just regular Rust library projects containing the module! macro.

flawless::module! { name = "example", version = "0.0.1" }

Let's use cargo to create a new Rust library project:

$ cargo new --lib hello-workflow
$ cd hello-workflow

1. Add flawless and log as a dependency to Cargo.toml

# ... truncated ...

[dependencies]
flawless = "1.0.0-beta.2"
log = "0.4"

2. Add a workflow to the module in src/lib.rs

Workflows in flawless are just regular Rust functions that are annotated with the #[workflow("<name>")] macro. You can have multiple of them inside one project.

Printing to standard output is not supported from a workflow, but you can use the log crate for logging. Flawless will set up a logging environment for each workflow automatically.

use flawless::workflow;
use log::info;

flawless::module! { name = "example", version = "0.0.1" }

#[workflow("hello_workflow")]
fn hello_workflow() {
    info!("👋 Hello from a workflow!");
}

And that's it! You have written your first workflow.

Deploying the workflow

To start this workflow, we first need to deploy it to the flawless server that we started earlier with flawless up.

You can simply deploy it by running the deploy command inside the project folder.

$ flawless deploy

This will compile the module to WebAssembly and upload it to the server.

Running the workflow

To run the workflow you can use the flawless run command:

$ flawless run example:0.0.1 hello_workflow

The workflow just prints out "👋 Hello from a workflow!" to the log. You can observe the output in the terminal window where you started flawless up.

The durability part of flawless

To demonstrate the durability part of Flawless, let's open the src/lib.rs file and replace the existing code with:

use std::time::Duration;

use flawless::{workflow, workflow::sleep};

flawless::module! { name = "example", version = "0.0.2" }

#[workflow("hello_workflow")]
fn hello_workflow() {
    for i in 0..30 {
        log::info!("👋 Hello from a workflow! {i}");
        sleep(Duration::from_secs(2));
    }
}

First we need to redeploy the modified workflow:

$ flawless deploy

Then we trigger the new workflow, specifying it's the version 0.0.2:

$ flawless run example:0.0.2 hello_workflow

This workflow runs for 1 minute and just prints a greeting message every 2 seconds.

To test the durability, you can abruptly stop the server with Ctrl+C or using the killall flawless command. And next time you bring up the server again with flawless up, the execution should pick up exactly where it stopped.

This is a very important property of Flawless. It implicitly "remembers" the exact point in code where the execution was interrupted, but it does so by only saving the minimal amount of information to reconstruct the state. Every other feature that Flawless has is a result of this property.

Workflow performing HTTP requests

Let's look now at a bit more complicated workflow that performs HTTP requests, fetches 10 dad jokes and logs them.

First we need to add another dependency to flawless, an HTTP client:

[dependencies]
flawless = "1.0.0-beta.2"
flawless-http = "1.0.0-beta.2"
log = "0.4"

Now let's update the src/lib.rs file with the implementation:

use std::time::Duration;

use flawless::idempotent::Idempotence;
use flawless::{workflow, workflow::sleep};
use flawless_http::get;

flawless::module! { name = "jokes", version = "1.0.0" }

#[workflow("dad_joke")]
fn dad_joke() {
    for i in 1..11 {
        let request
            = get("https://icanhazdadjoke.com/")
                .set_header("Accept", "application/json");
        let response = request.idempotent().send().unwrap();
        let response_txt
            = String::from_utf8(response.body()).unwrap();
        log::info!("Dad Joke {i}: {}", response_txt.trim());
        sleep(Duration::from_secs(1));
    }
}

Notice that the name of the module and workflow also changed. You can start it using:

$ flawless deploy
$ flawless run jokes:1.0.0 dad_joke

You will see the fetched jokes being printed out in the server window.

Next steps

Writing small workflows and triggering them from the command line is fun, but you probably would like to integrate flawless into bigger projects. In the next chapter we show how to integrate Flawless with your existing Rust application.