Skip to main content
Version: v2.0.0-rc

Develop a Wasm Component

Once you know what your application needs to be able to do, you can add language-agnostic interfaces for common capabilities like HTTP, key-value storage, or logging.

In this step, we'll discuss how to:

  • Update our application to accept a name and return a personalized greeting.
  • Add more features to our application by plugging in key-value and logging capabilities.
Prerequisites

This tutorial assumes you're following directly from the previous tutorial. If you don't have a "Hello world" application running with wash dev, complete Quickstart first.

Add functionality

Let's extend this application to do more than just say "Hello!"

Using methods on the incoming http::Request, we can check the request for a name provided in a query string, and then return a greeting with that name. If there isn't one or the path isn't in the format we expect, we'll default to saying "Hello, World!"

rust
use wasmcloud_component::http;

struct Component;

http::export!(Component);

impl http::Server for Component {
    fn handle(
        _request: http::IncomingRequest, 
        request: http::IncomingRequest, 
    ) -> http::Result<http::Response<impl http::OutgoingBody>> {
        let (parts, _body) = request.into_parts(); 
        let query = parts
            .uri
            .query()
            .map(ToString::to_string)
            .unwrap_or_default();
        let name = match query.split("=").collect::<Vec<&str>>()[..] {
            ["name", name] => name,
            _ => "World",
        };
        Ok(http::Response::new(format!("Hello, {name}!\n")))
        Ok(http::Response::new("Hello from Wasm!\n")) 
    }
}

After saving your changes, wash dev automatically builds and runs the updated application.

We can curl the application again:

shell
curl localhost:8000
text
Hello, World!
shell
curl 'localhost:8000?name=Bob'
text
Hello, Bob!

Add persistent storage

Now let's add persistent storage to keep a record of each person that this application greeted.

We'll use the key-value capability for this. We don't need to pick a library or a specific vendor implementation—all we have to do is add the interface to our component.

We can use the wasi:keyvalue interface for interacting with a key value store, and the wasi:logging interface to log the name of each person we greet. Before we can use those interfaces, we'll need to add them to our wit/world.wit file:

wit
package wasmcloud:hello;

world hello {
  import wasi:keyvalue/atomics@0.2.0-draft; 
  import wasi:keyvalue/store@0.2.0-draft; 
  import wasi:logging/logging@0.1.0-draft; 

  export wasi:http/incoming-handler@0.2.0;
}

We've given our application the ability to perform atomic incrementation and storage operations via the wasi:keyvalue interface and general logging operations via wasi:logging.

Now let's use the atomic increment function to keep track of how many times we've greeted each person.

rust
use wasmcloud_component::http; 
use wasmcloud_component::http::ErrorCode; 
use wasmcloud_component::wasi::keyvalue::*;
use wasmcloud_component::{http, info};

struct Component;

http::export!(Component);

impl http::Server for Component {
    fn handle(
        request: http::IncomingRequest,
    ) -> http::Result<http::Response<impl http::OutgoingBody>> {
        let (parts, _body) = request.into_parts();
        let query = parts
            .uri
            .query()
            .map(ToString::to_string)
            .unwrap_or_default();
        let name = match query.split("=").collect::<Vec<&str>>()[..] {
            ["name", name] => name,
            _ => "World",
        };

        info!("Greeting {name}"); 

        let bucket = store::open("default").map_err(|e| {
            ErrorCode::InternalError(Some(format!("failed to open KV bucket: {e:?}")))
        })?;
        let count = atomics::increment(&bucket, &name, 1).map_err(|e| {
            ErrorCode::InternalError(Some(format!("failed to increment counter: {e:?}")))
        })?;

        Ok(http::Response::new(format!("Hello x{count}, {name}!\n")))
    }
}

We've made changes, so once we save, wash dev will once again automatically update the running application.

Let's curl the component again:

shell
curl 'localhost:8000?name=Bob'
text
Hello x1, Bob!
shell
curl 'localhost:8000?name=Bob'
text
Hello x2, Bob!
shell
curl 'localhost:8000?name=Alice'
text
Hello x1, Alice!

Next steps

In this tutorial, you added a few more features and persistent storage to a simple microservice. You also got to see the process of developing with capabilities, where you can...

  • Write purely functional code that doesn't require you to pick a library or vendor upfront
  • Change your application separately from its non-functional requirements

So far, the wash dev process has satisfied our application's capability requirements automatically, so we can move quickly and focus on code. In the next tutorial, we'll deploy WebAssembly workloads to wasmCloud on a local Kubernetes cluster.