We are excited to announce the release of Slight v0.3, the latest version of our WebAssembly runtime that utilizes SpiderLightning (also known as wasi-cloud) capabilities.
This new release includes an array of new features, such as
- HTTP client which allows you to make outbound requests to a URL,
- A new messaging capability that combines message queue and pub/sub capabilities into one,
- A SQL capability that allows you to query databases with SQL.
and much more.
Check out our repository to find example Slight applications and see what you can create. To get started with Slight v0.3, simply download the latest release by running the following command provided below.
If you are using Linux or MacOS, run:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/deislabs/spiderlightning/main/install.sh)"
If you are using Windows, run:
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/deislabs/spiderlightning/main/install.ps1'))
Now, let’s dive in and see what new features are included in this release!
HTTP Client
Previously, Slight only supports the ability to serve HTTP requests. You can write applications in Rust or C to handle incoming HTTP requests and compile them to Wasm modules. With this release, Slight now supports the ability to make an outbound HTTP request to a URL. Take the following Rust program as an example:
wit_bindgen_rust::import("wit/http-client.wit");
#[register_handler]
fn handle_hello(_req: Request) -> Result<Response, HttpError> {
let req = crate::http_client::Request {
method: crate::http_client::Method::Get,
uri: "https://some-random-api.ml/facts/dog",
headers: &[],
body: None,
params: &[],
};
let res = crate::http_client::request(req).unwrap();
println!("{:?}", res);
let res = Response {
status: res.status,
headers: res.headers,
body: res.body,
};
Ok(res)
}
You can use wit-bindgen
tool to generate guest binding from http-client.wit
WIT file.
💡 The WIT file can be downloaded using
slight add http-client@v0.3.2
The handle_hello
function is responsible for handling incoming HTTP requests. It achieves this by creating a Request
object from the generated http_client
module, which is then sent using the request
function. The request
function makes an HTTP request to the URI defined in the Request
struct.
It is worth noting that the request
function is also generated. Its purpose is to handle the actual sending of the Request
object. Once the request
function has completed this task, it returns a Response
object, which serves as the return value for the handle_hello
function.
Developers can easily interface with the HTTP request processing mechanism by using the handle_hello
function. They can be confident that their HTTP requests will be handled appropriately. This function is a crucial part of any HTTP-based application.
Lastly, to enable both HTTP
and HTTP-client
capabilities, we need to update the configuration file. Below is an example slightfile.toml file.
specversion = "0.2"
[[capability]]
resource = "http"
name = "my-rest-api"
# This capability does not require any configs
[[capability]]
resource = "http-client"
name = "something"
Sending and Receiving Messages
In this new release, we have combined the message-queue
and pub/sub
capabilities into one called messaging
. The idea behind this move is that we think there are many overlaps and ambiguities to both capabilities and it made a difficult time for developers to choose one over another. Read’s Dan’s proposal explaining the design of the new messaging capability for more details.
In short, the new messaging capability interface looks like the following in WIT.
// producer interface
resource pub {
/// creates a handle to a pub object
static open: func(name: string) -> expected<pub, messaging-error>
/// publish a message to a topic
publish: func(msg: list<u8>, topic: string) -> expected<unit, messaging-error>
}
/// consumer interface
resource sub {
/// creates a handle to a sub object
static open: func(name: string) -> expected<sub, messaging-error>
/// subscribe to a topic
subscribe: func(topic: string) -> expected<subscription-token, messaging-error>
/// pull-based message delivery
receive: func(sub-tok: subscription-token) -> expected<list<u8>, messaging-error>
}
💡 The WIT file can be downloaded using
slight add messaging@v0.3.2
A publisher service can target the pub
resource to send messages to a topic. Similarly, a subscriber service can choose to subscribe to a topic and then pull the broker to receive messages. This way, the publisher and subscriber services can communicate with each other through the broker, without needing to know each other’s details.
Below is an example in Rust showing how you can write a simple publisher service that compiles to Wasm. In this example, the service sends a message to a topic named “rust” and “global-chat” with the messages:
use messaging::*;
wit_bindgen_rust::import!("../../wit/messaging.wit");
wit_error_rs::impl_error!(messaging::MessagingError);
fn main() -> Result<()> {
let ps = Pub::open("my-messaging")?;
for i in 0..3 {
println!("sending messages");
ps.publish(format!("rust-value-{}", i).as_bytes(), "rust")?;
ps.publish(format!("gc-value-{}", i).as_bytes(), "global-chat")?;
thread::sleep(Duration::from_secs(3));
}
Ok(())
}
And an example showing how a subscriber service could be authored:
let ps = Sub::open("my-messaging")?;
let sub_tok = ps.subscribe("rust")?;
let sub_tok1 = ps.subscribe("global-chat")?;
for _ in 0..3 {
loop {
let msg = ps.receive(&sub_tok)?;
if !msg.is_empty() {
println!("received message from topic 'rust'> value: {:?}", String::from_utf8(msg));
break;
}
}
loop {
let msg = ps.receive(&sub_tok1)?;
if !msg.is_empty() {
println!("received message from topic 'global-chat'> value: {:?}", String::from_utf8(msg));
break;
}
}
}
⚠️ Important Note:
One limitation of the current messaging interface and implementation is the lack of support for streaming events. This can be problematic in situations where the receive function is a blocking call that tries to pull the message broker for a new message. If the broker does not have any new messages, the receive function will be blocked, which can cause delays in message delivery. Additionally, the lack of streaming support can limit the scalability of the system, as the blocking call can cause bottlenecks in high-traffic scenarios. To address this limitation, we will be adding time-to-expire
to the receive function in the near future and will be collaborating with the Wasm Component Model community to land streams
feature in WIT.
Using the SQL Capability
The latest release from Slight adds an exciting new capability to its portfolio of capabilities. The newly added SpiderLightning functionality enables WebAssembly programs to easily and generically interact with SQL databases. This capability is not only highly versatile, but also secure, as it includes functions for querying and modifying data using prepared statements, as well as handling errors in a way that ensures data integrity.
Below is an example of using the SQL service in Slight.
use sql::*;
wit_bindgen_rust::import!("wit/sql.wit");
wit_error_rs::impl_error!(sql::SqlError);
fn main() -> Result<()> {
let sql = Sql::open("my-db")?;
// create table if it doesn't exist
sql.exec(&sql::Statement::prepare(
"CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL)",
&[],
))?;
// add new user
let rng = RNG::new(&Language::Elven).unwrap();
let name = rng.generate_name();
sql.exec(&sql::Statement::prepare(
"INSERT INTO users (name) VALUES (?)",
&[&name],
))?;
// get all users
let all_users = sql.query(&sql::Statement::prepare(
"SELECT name FROM users",
&[],
))?;
dbg!(all_users);
// get one user
let one_user = sql.query(&sql::Statement::prepare("SELECT name FROM users WHERE id = ?", &["2"]))?;
dbg!(one_user);
// try sql injection
assert!(sql
.query(&sql::Statement::prepare("SELECT name FROM users WHERE id = ?", &["2 OR 1=1"]))
.is_err());
Ok(())
}
💡 The WIT file can be downloaded using
slight add sql@v0.3.2
The SQL interface is paired with a slight configuration file to tell the host which SQL service is needed to provide this capability. Below is a sample slightfile.toml
using the PostgreSQL database implementation, which is the only service Slight supports right now.
specversion = "0.2"
[[capability]]
resource = "sql.postgres"
name = "my-db"
[capability.configs]
POSTGRES_CONNECTION_URL = "${azapp.POSTGRES_CONNECTION_URL}"
Thank you
We would like to take this opportunity to express our gratitude to all new contributors who have contributed to Slight!
As we mentioned in our Introducing SpiderLightning - A Cloud System Interface with WebAssembly blog, we are excited for the future of WebAssembly Component Model and WASI. We firmly believe that the work the Bytecode Alliance has done will enable all developers to build more portable and much more secure applications that can run anywhere! Thank you, for all the Bytecode Alliance contributors and maintainers for their work!