Accessing external APIs from Spin applications

Whether you are building a Discord bot with Spin or being a little more experimental, chances are you’ll probably want to have the ability to make outbound HTTP calls if you’re a Spin user.

Spin provides an extension that makes it simple to work with REST services. In this blog post, we’re going to show you how to make use of outbound HTTP calls to access external APIs using the wasi-experimental-http crate in Rust.

Similar examples also exists in the SDKs for Go and AssemblyScript.

Adding networking to WASI

At it’s core, WebAssembly relies on the idea of an isolated and sandboxed environment that is separate from the host environment. WASI expands upon this by providing a modular interface that enables a set of systems APIs specific to WebAssembly. There is currently a proposed networking API that is under consideration, but until it is agreed upon and implemented, WASI does not have the ability to open outbound network sockets.

The Fermyon Solution

We are in favor of a stronger security stance on networking. We like the idea of providing a high-level HTTP library with a permissions-based capability wrapped around it. To illustrate, we’ve developed an experimental API that enables outbound HTTP requests which we’ll dive into in this section. The API is available in a variety of languages but for this post we’re going to be leveraging Rust. Let’s get started.

Clone the project repo

To get started, we’re going to need to clone a repo that has a variety of examples demonstrating various Spin HTTP components in different programming languages:

$ git clone https://github.com/fermyon/spin-kitchensink.git

If you want to learn more about a component, dive into the README and navigate to the corresponding section. For this post, we’re going to be using the rust-outbound-http component that enables us to send outbound HTTP requests with Rust:

cd rust-outbound-http

Set up the component

To use this component, we’re going to need to define the spin.toml file which allows us to define the characteristics of our Spin component. Open the config file in your preferred text editor:

nano spin.toml

For this post, we’re going to be using the Dogs API to request information about various dog breeds via HTTP. To do this we’re going to need to add the domain of the API into the allowed_http_hosts section of the component:

[[component]]
id = "rust-outbound-http"
source = "rust-outbound-http/target/wasm32-wasi/release/rust_outbound_http.wasm"
# set the service URL as an environment variable pointing to the previous component
environment = { SERVICE_URL = "https://api.thedogapi.com/v1/breeds/2" }

# add the domain of the service URL to the list of allowed hosts, so the component 
# is allowed to send a request to it.
allowed_http_hosts = [ "https://api.thedogapi.com/" ]

You’ll also notice a field for SERVICE_URL where we will have to add the path of the API request we are attempting to call. After we’ve saved our spin.toml, we should be ready to build and deploy our component.

Building the component

We’re now ready to build the wasm module that we will eventually be using in our Spin component. To do this, we’ll need to install the Rust package manager cargo to build a release. You’ll need to make sure you install Rust so you can use Cargo’s features:

cargo build --release --target wasm32-wasi

We’re now ready to build the wasm module that we will eventually be using in our Spin component. To do this, we’ll need to use cargo to build a release:

cargo build -r --target wasm32-wasi

Once we have our module built, we should be ready to deploy with Spin.

Running and testing with Spin

Let’s go back and take another look at the source definition in our spin.toml file:

source = "rust-outbound-http/target/wasm32-wasi/release/rust_outbound_http.wasm"

Before we attempt to deploy with Spin, we need to make sure that our .wasm binary is at the path we have defined in this field. If everything looks good, we should be able to run spin up and be greeted with a response like this:

Serving HTTP on address http://127.0.0.1:3000

Now that our component is running, we’ll want to test it by cURLing localhost:3000 at the /outbound route we defined in our `spin.toml:

curl -i localhost:3000/outbound

We should get a response that looks something like this:

{
  "weight": {
    "imperial": "50 - 60",
    "metric": "23 - 27"
  },
  "height": {
    "imperial": "25 - 27",
    "metric": "64 - 69"
  },
  "id": 2,
  "name": "Afghan Hound",
  "country_code": "AG",
  "bred_for": "Coursing and hunting",
  "breed_group": "Hound",
  "life_span": "10 - 13 years",
  "temperament": "Aloof, Clownish, Dignified, Independent, Happy",
  "origin": "Afghanistan, Iran, Pakistan",
  "reference_image_id": "hMyT4CDXR"
}

Conclusion

The WebAssembly ecosystem is going through a massive shift as the community begins to embrace the idea of server-side Wasm having potential as the next phase in cloud-native computing. If you’re interested in joining a community of WebAssembly experts and enthusiasts, you should consider joining our Discord server! We have a variety of activities such as themed technical channels, exposure to subject matter experts, and interactive events for the community.

Join here!

Interested in learning more?

Get Updates