diff --git a/sdk/rust/README.md b/sdk/rust/README.md index b505d676a..1e23a4e36 100644 --- a/sdk/rust/README.md +++ b/sdk/rust/README.md @@ -2,80 +2,111 @@ The Spin Rust SDK makes it easy to build Spin components in Rust. -### Writing Spin HTTP components in Rust +## Fermyon Developer Home + +This `README` file provides a few examples, such as writing Spin HTTP components in Rust and making outbound HTTP requests. For comprehensive information, visit the official [Fermyon Developer Home](https://developer.fermyon.com/). This resource includes [a page on installing Spin](https://developer.fermyon.com/spin/v2/install#installing-spin), [a quickstart guide](https://developer.fermyon.com/spin/v2/quickstart), and [a language support overview page](https://developer.fermyon.com/spin/v2/language-support-overview). The latter lists all of Spin's features—including key-value storage, SQLite, MySQL, Redis, Serverless AI, etc.—and their implementation in specific languages such as Rust, TS/JS, Python, and TinyGo. + +### Writing Spin HTTP Components in Rust This library simplifies writing Spin HTTP components. Below is an example of such a component: ```rust // lib.rs -use anyhow::Result; -use spin_sdk::{ - http::{Request, Response}, - http_component, -}; +use spin_sdk::http::{IntoResponse, Request, Response}; +use spin_sdk::http_component; /// A simple Spin HTTP component. #[http_component] -fn hello_world(req: Request) -> Result { - println!("{:?}", req.headers); - Ok(Response::new_with_headers(200, &[] "Hello, Fermyon!")) +fn handle_hello_world(req: Request) -> anyhow::Result { + println!("Handling request to {:?}", req.header("spin-full-url")); + Ok(Response::builder() + .status(200) + .header("content-type", "text/plain") + .body("Hello, Fermyon") + .build()) } ``` -The important things to note in the function above: +The important things to note about the function above are: -- the `spin_sdk::http_component` macro — this marks the function as the - entrypoint for the Spin component -- the function signature — `fn hello_world(req: Request) -> Result` — -`req` can be any number of types including the built in `Request` type or -the `http::Request` from the popular `http` crate. Likewise, the response type -can be many things including the built in `Response` type or the `http::Response` type -from the `http` crate. +- the `spin_sdk::http_component` macro marks the function as the entry point for the Spin component, +- in the function signature (`fn handle_hello_world(req: Request) -> anyhow::Result`), `req` can be any number of types, including the built-in `Request` type or the `http::Request` type from the popular `http` crate +- in the function signature, the response type can be anything that implements `IntoResponse` meaning the return type can any number of things including `anyhow::Result` (as shown above), `impl IntoResponse`, `Response`, `anyhow::Result`, or even the `http::Response` type from the `http` crate. + - Note: Using the `http` crate will require you to add it to your Cargo.toml manifest (i.e., `cargo add http`). -### Making outbound HTTP requests +### Making Outbound HTTP Requests -Let's see an example where the component makes an outbound HTTP request to a -server, modifies the result, then returns it: +Let's see an example where the component makes an outbound HTTP request to a server, modifies the result, and then returns it: ```rust +use spin_sdk::{ + http::{IntoResponse, Request, Method, Response}, + http_component, +}; + #[http_component] -async fn hello_world(_req: Request) -> Result { - let mut res: http::Response = spin_sdk::http::send( - http::Request::builder() - .method("GET") - .uri("https://fermyon.com") - .body(())?, - ).await?; +async fn handle_hello_world(_req: Request) -> Result { + // Create the outbound request object + let req = Request::builder() + .method(Method::Get) + .uri("https://random-data-api.fermyon.app/animals/json") + .build(); - res.headers_mut() - .insert(http::header::SERVER, "spin/0.1.0".try_into()?); + // Send the request and await the response + let res: Response = spin_sdk::http::send(req).await?; + println!("{:?}", res); // log the response Ok(res) } - ``` -In order for the component above to be allowed to make the outbound HTTP -request, the destination host must be declared in the Spin application -configuration: +For the component above to be allowed to make the outbound HTTP request, the destination host must be declared, using the `allowed_outbound_hosts` configuration, in the Spin application's manifest (the `spin.toml` file): ```toml -[[component]] -id = "hello" -source = "target/wasm32-wasi/release/spinhelloworld.wasm" -allowed_outbound_hosts = [ "https://fermyon.com" ] -[component.trigger] -route = "/hello" +spin_manifest_version = 2 + +[application] +name = "hello_world" +version = "0.1.0" +authors = ["Your Name "] +description = "An example application" + +[[trigger.http]] +route = "/..." +component = "hello-world" + +[component.hello-world] +source = "target/wasm32-wasi/release/hello_world.wasm" +allowed_outbound_hosts = ["https://random-data-api.fermyon.app"] +[component.hello-world.build] +command = "cargo build --target wasm32-wasi --release" +watch = ["src/**/*.rs", "Cargo.toml"] ``` -Making a request to this component, we can see the appended header, and that the -response contains the expected body: +### Building and Running the Spin Application + +Spin build can be used to build all components defined in the Spin manifest file at the same time, and also has a flag that starts the application after finishing the compilation, `spin build --up`: + +```bash +$ spin build --up +Building component hello-world with `cargo build --target wasm32-wasi --release` + Finished release [optimized] target(s) in 0.12s +Finished building all Spin components +Logging component stdio to ".spin/logs/" +Serving http://127.0.0.1:3000 +Available Routes: + hello-world: http://127.0.0.1:3000 (wildcard) ``` -$ curl -I localhost:3000/hello + +Once our application is running, we can make a request (by visiting `http://localhost:3000/` in a web browser) or using `curl` as shown below: + +```bash +$ curl -i localhost:3000 HTTP/1.1 200 OK -content-length: 29350 -content-type: text/html; charset=utf-8 -server: spin/0.1.0 +content-length: 77 +content-type: application/json + +{"timestamp":1702599575198,"fact":"Sharks lay the biggest eggs in the world"} ```