Getting Started with Rust Cloudflare Workers
Tags: serverless, webdev, restapi, rust
Published: 2022-04-25 09:54:28

---
title: "Getting Started with Rust Cloudflare Workers"
published: "true"
description: "Getting started with Rust Cloudflare Workers: how you can build your first serverless Rust app including REST requests and sending email."
tags: "serverless, webdev, restapi, rust"
canonical_url: "https://rodneylab.com/getting-started-rust-cloudflare-workers/"
cover_image: "https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubq2ye5y2f9ylnjtd9we.png"
---

## ☁️ Why use Rust Cloudflare Workers?

In this article, we have a look at getting started with Rust Cloudflare Workers.  We build out a basic serverless API with router.  Before we get into that, though, you might be asking why use serverless Rust? Especially if you already know JavaScript.  After all, much of the functionality we would want to implement can also be implemented with JavaScript.  For me, however, the motivation is **learning Rust**.  Rust tooling is increasing prevalent, in the Web Development sphere.  Examples are **swc** and **Parcel.js**.  swc compiles TypeScript and JavaScript, and bundles twenty times faster than Babel.  Parcel.js saw a **10 times speed improvement** moving to the new Rust implementation.

Of course, you might not see these speed improvements in serverless functions.  That said, serverless functions are usually small pieces of code which perform a single small task.  To me, that makes them a great choice for learning Rust.  You can invest small chunks of your time on focussed code, starting with a simple Rust Cloudflare Worker.  Then either as the initial functions evolve and require more sophisticated solutions, or indeed, as you consider solving other more detailed problems using Workers, you can gradually improve your Rust understanding.

## 🧑🏽‍🎓 Learning in Public

I should point out I am still relatively new to Rust, so you might know of better Rust implementations of the Rust code below.  In fact, I would love feedback on how I could improve the Rust code (drop comments below or <a aria-label="Adda pull request to the demo code on Git Hub" href="https://github.com/rodneylab/serverless-rust/pulls">add pull requests to the demo code repo</a>).  Instead of best practice Rust, this is more of a guide on how to get up and running with Rust Cloudflare Workers, where I share some of the Rust I have learned.  That said, I hope you can benefit from the post if you already know Rust, but want to know how to get going with Rust Cloudflare Workers.

### 📚 Rust Learning Resources

If you are learning Rust, here are some free resources you might find useful:

- <a aria-label="Open the Rust book" href="https://doc.rust-lang.org/stable/book/">The Rust Programming Language book</a> &mdash; normally just referred to as &ldquo;**The Book**&rdquo; is probably the best starting point.  Available online as a number of chapters, it offers a gentle introduction to Rust.  Also try <a href="https://doc.rust-lang.org/rust-by-example/index.html">Rust by Example</a>, to help push home concepts in the Book, especially if you prefer a more hands-on approach to learning.
- <a aria-label="Open the Rustlings Git Hub repo" href="https://github.com/rust-lang/rustlings">Rustlings</a> &mdash; a collection of small exercises you can run through to improve your Rust. Consider this if you prefer a more practical approach to learning, rather than reading the book.
- <a aria-label="Open the Rust Cookbook" href="https://rust-lang-nursery.github.io/rust-cookbook/intro.html">Rust Cookbook</a> &mdash; you will probably use this differently to the other two resources. Rather than work through from start to finish, it is quite handy to dive in to a particular area you need for a problem you are looking at, which you have not yet seen in Rust.

If you prefer videos, <a aria-label="Open Chris Biscardi's Twitter profile" href="https://twitter.com/chrisbiscardi">Chris Biscardi</a> is behind <a aria-label="Open Rust Adventures" href="https://www.rustadventure.rs/">Rust Adventures</a> which provides another avenue to learning Rust.  You can access much of the content for free.  Chris is very knowledgable in Rust and I have, in fact, picked up quite a bit from his blog posts.

## 🧱 Getting Started with Rust Cloudflare Workers: What we&rsquo;re Building

We will start by scaffolding the new Rust Cloudflare Worker, setting up all the necessary tooling.  Once we have our hello, world running, we add a router with a health check route.  Adding more features, we learn a little more about use Rust with Workers, wrapping up with a final message API route which can listen for and parse JSON `POST` requests.

## ⚙️ Rust Setup

You can set up Rust with Homebrew or other package managers.  <a aria-label="Read Rust docs on Installing Rust" href="https://www.rust-lang.org/tools/install">Rust&rsquo;s recommended approach</a> though, is to install from the Terminal from their script:

```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```

This installs **rustup**, which manages your Rust installation.  You could use the following command to update your Rust tooling (when the next release is available, for example):

```shell
rustup update
```

For now, we want to be able to cross-compile Rust code into WASM, and will use `rustup` to add the tooling, locally for us:

```shell
rustup target add wasm32-unknown-unknown
```

## 🔧 Serverless Rust Cloudflare Worker Setup

Clone the Cloudflare Rust Worker template using <a href="https://pnpm.io/installation">pnpm</a>:

```shell
pnpm dlx wrangler generate \\
    getting-started-rust-cloudflare-workers \\
    https://github.com/cloudflare/workers-sdk/templates/experimental/worker-rust
```

You should now have a new `getting-started-rust-cloudflare-workers` directory containing:

- `src/lib.rs`: your Rust worker source file
- `Cargo.toml`: Rust&rsquo;s package meta file (analogous to Node&rsquo;s `package.json`)
- `package.json`: Node `package.json` file with worker development and deploy scripts
- `wrangler.toml`: the Wrangler config

### 📦 Package Meta

Start by customizing the `Cargo.toml` for our project:

```toml
[package]
name = "my-rust-cloudflare-worker"
version = "0.1.0"
edition = "2021"
authors = ["Robert Fullman <robert@example.com>"]
description = "My first Rust Cloudflare Worker"
repository = "https://github.com/example-profile/my-rust-cloudflare-worker"
license = "BSD-3-Clause"
```

Currently, the template does not include the last four lines shown, but you may like to include them, to follow best practice (adapting to suit your needs).  Also, update the `name` field to match the project name.

### 🖥 Dev environment

To install Wrangler locally, within the project, from your project directory, run the command:

```shell
pnpm install
```

Wrangler is Cloudflare&rsquo;s worker tooling.  Fire up the dev environment from the Terminal:

```shell
wrangler dev
```

![pnpm dev: screenshot shows Terminal with output showing the worker is listening on 0.0.0.0:8787](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lf3m9no5zeiry0t1i15a.png)

Cargo will compile the code in `src/lib.rs`.  This will be a little slow this first time.  When compilation completes, you will see a message like `Listening on 0.0.0.0:8787` in the Terminal.  With Wrangler running, you can jump to your browser by pressing the <kbd>B</kbd> key, or alternatively typing `http://127.0.0.1:8787` in your preferred browser address bar. Either way, you should get a &ldquo;Hello, World&rdquo; message.

![Getting Started with Rust Cloudflare Workers: Wrangler Update: screenshot shows a browser window with the words "Hello, World!" in white on a black background.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ko6adun2b3s1je6gv1cl.png)

### 🦀 Rust Coding

It would be nice to get a little log printed to the console on each request.  Update the Rust source code in `src/lib.rs`, adding a logging function:

```rust
use worker::*;

fn log_request(req: &Request) {
    console_log!(
        "{} - [{}], located at: {:?}, within: {}",
        Date::now().to_string(),
        req.path(),
        req.cf().coordinates().unwrap_or_default(),
        req.cf().region().unwrap_or("unknown region".into())
    );
}

#[event(fetch)]
async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
    log_request(&req);
    Response::ok("Hello, World!")
}
```

![Getting Started with Rust Cloudflare Workers: Wrangler Console Log: screenshot shows Terminal with wrangler running last lines log a recent request, showing timestamp, route (which is "/"), approximate user coordinates and country (England).](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vr6yvtzxf8qmb43ncopc.png)

Refresh the browser, and you should see a little log appear.  This is handy for debugging, though in production, you might <a href="/astro-js-middleware-example/">consider a serverless logging provider for your API, like Logtail</a>.

## 🚢 Cargo tools

A feature that sets Rust apart from other languages is the helpfulness of the compiler.  Although the code works as it is, try running:

```shell
cargo check
```

![Getting Started with Rust Cloudflare Workers: Cargo check: screenshot shows Terminal user has typed "cargo check". Feedback includes warnings on the env and ctx variables being unused.  Feedback is detailed with line numbers for the source code and clear annotations showing the exact word the warnings pertain to.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w286th5ahn3met9q7aub.png)

You will get some linting tips!  For next-level linting, try Clippy.  You won&rsquo;t see a difference for our short snippet, but generally it will go the extra mile in helping you write more idiomatic Rust.

```shell
rustup component add clippy
cargo clippy
```

The first command is a one-off piece of setup, installing Clippy, if it is not already on your system.

Finally, here is another tool in the Rust tool chain; this time for formatting your code:

```shell
cargo fmt
```

## 🛣️ Adding Some Routes

For an API, you will, typically, need more than a single route. We can set up the typical `health_check` route on a router for our worker:

```rust
#[event(fetch)]
async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
    log_request(&req);
    let router = Router::new();

    router
        .get("/health_check", |_, _| Response::ok("OK"))
        .run(req, env)
        .await
}
```

<a href="https://docs.rs/worker/0.0.17/worker/struct.Router.html">`Router` here comes from the `worker` crate</a>.  We do not need to import it explicitly as, in line `1` of `src/lib.rs`, we bring all structs from `worker` into scope.

## 🍃 Environment Variables

Rust procedural macros provide syntactic sugar for common tasks.  Let&rsquo;s see a couple here.  First, we can extract meta from the `Cargo.toml` file using the `env!` macro.  The current package version will be handy if you are putting together the API docs, for example.  Access the package version using `CARGO_PKG_VERSION`:

```rust
#[event(fetch)]
async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
    log_request(&req);
    let router = Router::new();

    router
        .get("/health_check", |_, _| Response::ok("OK"))
        .get("/api-version", |_,_| {
           let version = env!("CARGO_PKG_VERSION");
           Response::ok(format!("API version: {version}"))
        })
        .run(req, env)
        .await
}
```

Notice, we used `format!` (another procedural macro) to build the response using an interpolated string.

## 📮 `POST` Requests

We have only looked at REST `GET` requests so far.  Let&rsquo;s create a basic message route, listening for JSON `POST` requests.  If you are just starting out in Rust, you will quickly find one of the most useful crates (Rust packages) is Serde (a contraction of serialize and deserialize).  We will use it here to parse the JSON request body, and you will find there are YAML and plenty of other alternatives.

### Getting Started with Rust Cloudflare Workers: adding Dependencies

To add a crate to your project, you just update `Cargo.toml`, including the version number for the crate:

```toml
[dependencies]
serde = "1"
worker = "0.0.15"
 
# TRUNCATED...
```

Currently, `cargo fmt` will format Rust source only and not `Cargo.toml`, but you can <a aria-label="install and configure the d print code formatter" href="https://dprint.dev/install/">install and configure the dprint code formatter</a> (written in Rust) if you want that formatted too:

```shell
cargo install --locked dprint
```

### Rust `derive` Procedural Macros

Serde uses a `derive` macro; this is a different type of <a href="https://doc.rust-lang.org/reference/procedural-macros.html">Rust procedural macro</a> to `env!` and `format!` **function-like** procedural macros seen earlier.  `derive` macros again provide syntactic sugar; under the hood, our convenient, clean code will get re-written, by Serde, to something more verbose and a little unwieldy! 

To bring the Serde `Deserialize` macro into scope, add this line at the top of `src/lib.rs`:

```rust
use serde::Deserialize;
use worker::*;
```

Next, with Rust being a typed language, we want to define the types for the JSON data we are expecting to receive.  Add this struct in `src/lib.rs` defining the `Message`:

```rust
#[derive(Deserialize)]
struct Message {
    name: String,
    email: String,

    #[allow(dead_code)]
    message: String,
}
```

In production code, we would use `message` as well as `name` and `email`.  However, here we just do a quick console log.  The line: `#[allow(dead_code)]` is an instruction to Cargo linting tools to ignore the fact that `message` exists on the `Message` struct, but is never used.

If you are curious to see what the expanded  `derive` macro code looks like, <a href="https://github.com/dtolnay/cargo-expand">set up and run the `cargo-expand` tool</a>.  This uses some unstable Rust features, so run `rustup toolchain install nightly --allow-downgrade` to make the nightly toolchain available locally before running `cargo expand` for the first time.

### POST Route Code

Finally, add a `POST` route to the `main` function, to listen for messages:

```rust
#[event(fetch)]
async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
    log_request(&req);
    let router = Router::new();

    router
        .get("/health_check", |_, _| Response::ok("OK"))
        .get("/api-version", |_, _| {
            let version = env!("CARGO_PKG_VERSION");
            Response::ok(format!("API version: {version}"))
        })
        .post_async("/message", |mut req, _| async move {
            let Message { name, email, .. } = match req.json().await {
                Ok(value) => value,
                Err(_) => return Response::error("Bad request", 400),
            };
            console_log!("New message from {name} ({email})");
            Response::ok(format!("Thanks {name}, we'll be in touch!"))
        })
        .run(req, env)
        .await
}
```

We use a `console_log!` macro provided by the worker crate to print the message to the console.  In general, in Rust code, you will use `println!` or <a aria-label="d b g" href="https://doc.rust-lang.org/std/macro.dbg.html">`dbg!`</a> instead.  Be careful no to log Personal Identifiable Information (**PII**), and be sure to comply with legal requirements.  We only include the fictitious email here to demonstrate Rust features.  Be sure to protect any real user data you work with.  You might <a href="https://docs.rs/secrecy/">consider using the secrecy crate for such private data</a>, which will help you obfuscate PII, by default, and opt-in to reveal the data to authorized users.

## 💯 Getting Started with Rust Cloudflare Workers: Check your Work

You can check this new `POST` route work by sending a message using curl in a separate tab:

```shell
curl http://localhost:8787/message \\
  -H "Content-Type: application/json" \\
  -d '{
	"name": "Margaret Falls",
	"email": "margaret@example.com",
	"message": "Hello, everybody!"
}'
```

![Getting Started with Rust Cloudflare Workers: Message Log: screenshot shows Terminal running Wrangler.  A recent request log is visible, with request details listed. Below, on a separate line, is the message "New message from Margaret Falls (margaret@example.com)"](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwaowxghd1of1hvs9djz.png)

## 🙌🏽 Getting Started with Rust Cloudflare Workers: Wrapping Up

We have learned all about getting started with Rust Cloudflare  Workers in this post.  More specifically, we have seen:

- how to set up **Cloudflare Rust worker tooling**;
- how you can **create a Rust worker API router**; and
- some **worker features like `console_log`**.

The <a aria-label="Open the Astro JS tutorial code in the Rodney Lab Git Hub Repo" href="https://github.com/rodneylab/serverless-rust/tree/main/demos/getting-started-rust-cloudflare-workers">Getting started with Rust Cloudflare Workers demo code is in the Rodney Lab GitHub repo</a>.

I hope you found this article useful and am keen to hear where you will go next, as well as potential improvements, both to the Rust code and explanations above.

## 🙏🏽 Getting Started with Rust Cloudflare Workers: Feedback

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please <a aria-label="Support Rodney Lab via Buy me a Coffee" href="/giving/">consider supporting me through Buy me a Coffee</a>.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via <a aria-label="@ask Rodney" href="https://twitter.com/messages/compose?recipient_id=1323579817258831875">@askRodney</a> on Twitter and also <a aria-label="Contact Rodney Lab via Telegram" href="https://t.me/askRodney">askRodney on Telegram</a>. Also, see <a aria-label="Get in touch with Rodney Lab" href="/contact/">further ways to get in touch with Rodney Lab</a>. I post regularly on <a aria-label="See posts on Astro" href="/tags/astro/">Astro</a> as well as <a aria-label="See posts on svelte kit" href="/tags/sveltekit/">SvelteKit</a>. Also, <a href="https://newsletter.rodneylab.com/issue/latest-issue">subscribe to the newsletter to keep up-to-date</a> with our latest projects.