February 21, 2023

Announcing Spin v0.9.0

Radu Matei Radu Matei

spin wasm wasi

Announcing Spin v0.9.0

Spin is an open source developer tool for building and running serverless functions and microservices with WebAssembly. Since we launched Spin, one of our goals has been to rethink the way we are building and deploying modern software by using the amazing advances in the WebAssembly ecosystem such as the component model.

Today, we are happy to announce a new release of Spin, v0.9.0. This release brings a few new features and a host of improvements and fixes:

  • previewing a built-in key/value store
  • running a Spin application from a remote registry
  • a new Redis execute API that can send any command to a Redis database
  • a new upgrade experience for templates
  • and more!

Let’s have a look at a few of the new things in this release!

Previewing Spin Key/Value Support

At its core, Spin is best suited for stateless, request/response types of workloads. This means that if your application needs to persist state, you need to manage that with an external tool or service.

In this version, we are introducing a new API in the Spin SDKs for persisting and retrieving non-relational data from a key/value store across multiple requests to the same application. Together with the updated SDKs we are introducing a default, built-in, local key/value store available for every Spin application with minimal configuration.

Accessing a store can be done directly through a Spin SDK, and a store currently implements the following functionality:

  • checking whether a key exists
  • listing all the keys
  • getting the value of a key
  • setting a new key

This feature is built in collaboration with the upstream WebAssembly proposal for key/value storage, and the intention is for the APIs to be fully compatible.

Let’s explore an example — we want to persist some serialized data across requests of the same application — a view counter that we increment on each request, and a field that contains the path of the last request. We open the “default store” for our application — a special store that every environment running Spin applications will make available for their application (which when running Spin applications locally, is built using SQLite).

We can now use the default store to persist data. Here is a complete Rust example:

// key for the application state
const DATA_KEY: &str = "app-data";

// application state
#[derive(Serialize, Deserialize, Default)]
struct Data {
    views: u64,
    previous_request: String,
}

/// A simple Spin HTTP component.
#[http_component]
fn hello_kv(req: Request) -> Result<Response> {
    // open the default KV store
    let kv = Store::open_default()?;

    // check whether the key already exists
    let mut data = match kv.exists(DATA_KEY)? {
        // if it exists, get the value and deserialize it
        true => serde_json::from_slice(&kv.get(DATA_KEY)?)?,
        false => Data::default(),
    };

    // update the key/value pair using the new data
    data.views += 1;
    let body = serde_json::to_string(&data)?;
    data.previous_request = req.uri().path().into();
    // update the key/value pair using the new data
    kv.set(DATA_KEY, serde_json::to_vec(&data)?)?;

    // send the value as a response
    Ok(http::Response::builder()
        .status(200)
        .header("content-type", "application/json")
        .body(Some(body.into()))?)
}

There is one piece of configuration we need — explicitly granting a component access to a key/value store. This is because we want to restrict what components can access data, particularly as we start sharing components and using pre-built components from registries. This can be done by adding a new line to the component configuration, key_value_stores, which will reference all the stores we want the component to have access to — in this case, we only have a “default” store:

[[component]]
id = "kv-example"
# we need to explicitly grant this component access
# to the application's default KV store.
key_value_stores = ["default"]
...
[component.trigger]
route = "/rust/..."

Once we have completed this minimal configuration, spin build and spin up will commence persisting data across requests:

$ curl localhost:3000/rust/hello
{"views":1,"previous_request":""}

$ curl localhost:3000/rust/goodbye
{"views":2,"previous_request":"/rust/hello"}

$ curl localhost:3000/rust/hi-again
{"views":3,"previous_request":"/rust/goodbye"}

We can also configure multiple components in the same application to access the same store — currently only the default store. You can already use the new feature from the Rust, JavaScript, TypeScript, and Go SDKs for Spin. Here is a small example written in TypeScript:

const key = "app-data";

interface Data {
  views: number,
  previous_request: string
}

export async function handleRequest(request) {
  let kv = spinSdk.kv.openDefault();

  let data: Data;
  if (kv.exists(key)) {
    data = JSON.parse(decoder.decode(kv.get(key)))
  } else {
    data = { views: 0, previous_request: "" } as Data;
  }

  data.views += 1000;
  data.previous_request = request.uri;

  kv.set(key, JSON.stringify(data));
...
}

This is an early preview for working with key/value stores, and we want to get feedback on the ergonomics of the API (and what new APIs we should implement), as well as on what backing stores you would like to see. In the next iterations for this feature, we will focus on configuring multiple key/value stores with multiple backing services (such as cloud services).

We are really excited about this feature — when stable, you will be able to run the same application and configure different services for the various key/value stores that an application needs — such as SQLite when developing locally, an in-memory Redis instance for testing, and your favorite cloud service for key/value storage in production — all without recompiling your application. You can read the improvement proposal for key/value support as well as the the implementation for the current feature.

In parallel, we are hard at work building a best-in-class default key/value implementation in Fermyon Cloud and in other environments where you can run Spin applications. Stay tuned!

Running Spin Applications From Registries

In Spin v0.8.0 we added support for distributing Spin applications using Docker Hub, GitHub Container Registry, or ECR. In this version, we have refined that experience and added two major improvements: you can now directly run spin up and pass an application from a registry, and you can log in to a registry using the spin registry login command. Here is the current experience for distributing and running Spin applications using the GitHub registry:

# create a new Spin application and build it
$ spin new http-go hello-registries --accept-defaults && cd hello-registries && spin build
# log in to the GitHub registry
$ echo $GHCR_PAT | spin registry login --username radu-matei --password-stdin ghcr.io
# push the Spin application to the GitHub registry
$ spin registry push ghcr.io/radu-matei/hello-registries:v1
# run the Spin application directly from the GitHub registry
$ spin up --from-registry ghcr.io/radu-matei/hello-registries:v1

If you have Spin v0.9.0, you can execute the same spin up command on your machine and run the application directly from the registry.

Using existing registry services has been an outstanding feature request for Spin, and we are happy to begin stabilizing it. As this support matures, we will begin deprecating support for distributing applications using Bindle.

A New Redis execute API

Spin has had the ability to connect to Redis databases since the initial launch — and one of the features requested for working with Redis databases has been the ability to send arbitrary commands to a Redis instance. In Spin v0.9.0, we are adding a new Redis API that allows sending any command to Redis. Here is an example in Go:

res, _ := redis.Execute("redis://localhost:6379", "lrange", []redis.RedisParameter{encodeString("list"), encodeInt(0), encodeInt(-1)})

for _, item := range res {
    fmt.Fprintf(os.Stdout, "Result: %s\n", item.Val)
}

Notice that parameters have to be encoded, and results decoded based on their Redis data type. This API gives the most flexibility when working with Redis, and we are excited to continue to refine the experience for this set of APIs for all Spin SDKs.

A New Template Upgrade Experience

Templates in Spin are a crucial part of the spin new experience, and you can start a new Spin application based on any template with a few commands. This version of Spin overhauls the experience of upgrading old versions of templates configured locally. Now, the command prompts you to select the template repositories to upgrade:

$ spin templates upgrade
The following template repositories can be automatically upgraded.
Select repos to upgrade. Use Space to select/deselect and Enter to confirm selection.
> [ ] https://github.com/fermyon/spin (at spin/templates/v0.9)
  [ ] https://github.com/fermyon/spin-js-sdk (at spin/templates/v0.9)

Alternatively, you can upgrade all templates using the --all flag.

And More!

Even more improvements and bug fixes have made their way into Spin v0.9.0! Here are a few of them:

If you want to see the full changelog, head over on GitHub.

Building Towards Spin 1.0

Spin aims to empower developers to build and run real-world applications. A large part of this is providing sufficient functionality and capabilities; but equally important is using tools that are stable and performant. Over the next few releases, we will work towards Spin 1.0, with a focus on the stability of the spin new -> spin build -> spin up experience so that you can build and run Spin applications with confidence.

Spin 1.0 will also mean a promise of backwards compatibility — that you can confidently upgrade to a new minor version of Spin and know that your applications will continue to run regardless of where they are running. You can follow along the progress towards Spin 1.0 in our project board.

Thank you!

Spin would not be possible without the more than 50 contributors who are dedicating their time to write code and documentation, and without everyone using the project. Thank you!

We also want to give a special shout-out to all Bytecode Alliance project maintainers for their incredible work that is crucial for Spin — in particular the Wasmtime project and the people building WASI and the WebAssembly component model.

If you are interested in Spin, Fermyon Cloud, or other Fermyon projects, join the chat in the Fermyon Discord server and follow us on Twitter @fermyontech!


🔥 Recommended Posts


Quickstart Your Serveless Apps with Spin

Get Started