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:
- Spin is now using the latest version of Wasmtime, v5.0.0
- improved TinyGo SDK support
- the routing algorithm for HTTP applications has now changed so the longest matching prefix takes higher precedence than the ordering of the routes, and
spin up
warns if there are duplicate routes in your configuration spin build
now allows interactive commandsspin up
now logs to the console by default, and there is a--quiet
flag to prevent the logs from being sent to the consolespin oci
has now been renamed tospin registry
- components pulled from HTTP sources are now cached
- the JavaScript SDK now implements more functions from the
crypto
module - we have added a new set of commands for deploying applications to Fermyon,
spin cloud
. This is the first step in evolving the experience for deploying applications to Fermyon Cloud and other environments through plugins
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!