Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update readme to latest template syntax #2178

Merged
merged 10 commits into from
Dec 20, 2023
122 changes: 76 additions & 46 deletions sdk/rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,110 @@

The Spin Rust SDK makes it easy to build Spin components in Rust.

tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
### 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<Response> {
println!("{:?}", req.headers);
Ok(Response::new_with_headers(200, &[] "Hello, Fermyon!"))
fn handle_hello_world(req: Request) -> anyhow::Result<impl IntoResponse> {
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<Response>` —
`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<impl IntoResponse>`), `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<impl IntoResponse>` (as shown above), `impl IntoResponse`, `Response`, `anyhow::Result<Response>`, 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
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

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<Response> {
let mut res: http::Response<String> = spin_sdk::http::send(
http::Request::builder()
.method("GET")
.uri("https://fermyon.com")
.body(())?,
).await?;
async fn handle_hello_world(_req: Request) -> Result<impl IntoResponse> {
// 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 <[email protected]>"]
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`:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```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
```
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
content-length: 77
content-type: application/json

{"timestamp":1702599575198,"fact":"Sharks lay the biggest eggs in the world"}