DEV Community

Matt Butcher
Matt Butcher

Posted on • Updated on

Building a Serverless Python WebAssembly App with Spin

In this post, we'll build a simple Serverless (a.k.a. "Functions as a Service") app with WebAssembly. We'll be using the open source Wasm tool called Spin.

If you haven't already, you will need to install Spin. I'm using the version of Python that is already installed on my Mac.

I am using VS Code with the Spin and Python extensions. However, that is all optional.

The first thing to do is to create a new Spin application.

$ spin new http-py hello-spin
Description: Simple Hello example in Python
HTTP base: /
HTTP path: /...
Enter fullscreen mode Exit fullscreen mode

The above will create a new directory called hello-spin with a bunch of files automatically scaffolded out for us:

tree .
.
├── Pipfile
├── README.md
├── app.py
└── spin.toml

0 directories, 4 files
Enter fullscreen mode Exit fullscreen mode
  • Pipfile is the dependency file used by pip. This is pretty standard for Python projects.
  • README.md is a project description. When building your own projects, you may want to edit that to describe how to build and use your app.
  • app.py is the source code for our app. In a moment, we'll open and edit that file.
  • spin.toml is the configuration file Spin uses to learn how to construct and run your app. We'll take a quick look at that now.

Describing a serverless Wasm app with spin.toml

The spin.toml file looks like this:

spin_manifest_version = "1"
authors = ["Matt Butcher <matt.butcher@fermyon.com>"]
description = "Simple Hello example in Python"
name = "hello-spin"
trigger = { type = "http", base = "/" }
version = "0.1.0"

[[component]]
id = "hello-spin"
source = "app.wasm"
[component.trigger]
route = "/..."
[component.build]
command = "spin py2wasm app -o app.wasm"
watch = ["app.py", "Pipfile"]
Enter fullscreen mode Exit fullscreen mode

The first block of lines are all basic data about the app. Who wrote it, what version of the SDK to use, and so on. Usually we don't need to change anything in there other than perhaps writing your own description or changing the authors.

Next, we have a [[component]]. Spin applications are composed of one or more components. You can think of a component in a few different ways:

  1. As a microservice that is part of a bigger app (and gets deployed with that app)
  2. As a stand-alone Wasm binary that gets run alongside other binaries
  3. As a route on a routing table

In our case, we have one component named hello-spin. It is mapped to the route /... (which is a wildcard path meaning anything under the root). And when it is executed, it will run the (currently nonexistent) app.wasm.

There's a section called [component.build] which explains what happens to build this component when someone executes the project command spin build (which we'll do in a moment). You can see there that the build command is spin py2wasm app -o app.wasm. If we were writing a Rust or Javascript or Go app, the build command would be different, though most of the rest of this file would remain the same.

You can mix languages in a Spin app.

The watch line at the bottom tells the command spin watch which files it should watch for changes. We won't be using spin watch as we build our app below.

That's it for the spin.toml. Let's write some Python!

Coding a First Spin Python App

If we open app.py, we'll see that spin new already scaffolded us a basic Python app:

from spin_http import Response


def handle_request(request):

    return Response(200,
                    {"content-type": "text/plain"},
                    bytes(f"Hello from the Python SDK", "utf-8"))

Enter fullscreen mode Exit fullscreen mode

Spin builds serverless apps, which means we don't need to install or configure or start any sort of server in our code. Instead, the serverless runtime (Spin, in this case) will look for a specific function: handle_request(request). That function is expected to return an instance of the Response object that we imported. That's all there is to a Spin Python app!

Without altering a line, we can build:

$ spin build
Building component hello-spin with `spin py2wasm app -o app.wasm`
Spin-compatible module built successfully
Finished building all Spin components
Enter fullscreen mode Exit fullscreen mode

Now you should see an app.wasm file, which is what the spin.toml points to.

Spin can act as a runtime for serverless functions locally. Let's start a local server:

$ spin up             
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  hello-spin: http://127.0.0.1:3000 (wildcard)
Enter fullscreen mode Exit fullscreen mode

We point a web browser at http://127.0.0.1:3000 and see the results:

Screenshot of browser saying Hello from the Python SDK

To wrap up this intro tutorial, let's make a quick change and then rebuild and restart our app.

You can use CTRL-C to stop spin up

Here's a quick change to our code:

from spin_http import Response


def handle_request(request):

    return Response(200,
                    {"content-type": "text/plain"},
                    bytes(f"Hello Dev.to!", "utf-8"))
Enter fullscreen mode Exit fullscreen mode

All we've done is change the body to say Hello Dev.to. Now let's build and start locally with one quick command:

$ spin build --up
Building component hello-spin with `spin py2wasm app -o app.wasm`
Spin-compatible module built successfully
Finished building all Spin components
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  hello-spin: http://127.0.0.1:3000 (wildcard)
Enter fullscreen mode Exit fullscreen mode

Once more, we now have a local copy of the app running. And we can refresh our browser window and see the result:

Screenshot of browser showing hello dev.tom

From here, we could edit the app to do things like interact with the built-in Key Value Store or create some tables in the built-in SQL Database or even use the built-in LLM for some AI coding.

And whenever you are ready to deploy, you can run spin deploy to send it to Fermyon Cloud, or you can deploy it into Docker Desktop, Kubernetes, Nomad, or other environments.

Top comments (0)