Opens an external site in a new window
Pray for peace.
RODNEY LAB
  • Home
  • Plus +
  • Projects
  • Giving
  • Contact
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Contact

Getting Started with Rust Cloudflare Workers # Getting Started with Rust Cloudflare Workers #

Getting Started with Rust Cloudflare Workers
  1. Rodney Lab Home
  2. Rodney Lab Blog Posts
  3. Rust Blog Posts
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Getting Started with Rust Cloudflare Workers #

Published: 10 months ago
10 minute read Gunning Fog Index: 5.8
Content by Rodney
Author Image: Rodney from Rodney Lab
SHARE:

☁️ Why use Rust Cloudflare Workers? #

In this article we take a look at getting started with Rust Cloudflare Workers. We build out a basic serverless function which lets you send email. 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, the motivation is learning Rust. Rust is becoming more pervasive in the web development sphere, especially for modern tooling. Standalone Rust implemented tool 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 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 add pull requests to the demo code repo  ). 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:

  • The Rust Programming Language book  — normally just referred to as “The Book” is probably the best starting point. Available online as a number of chapters which offer a gentle introduction to Rust. Also try Rust by Example  to help push home concepts in the Book, especially if you prefer a more hands-on approach to learning,
  • Rustlings  — 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,
  • Rust Cookbook  — 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, Chris Biscardi  is behind Rust Adventures  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 picked up quite a bit from his blog posts.

🗳 Poll #

How familiar are you with Rust?
Voting reveals latest results.

🧱 Getting Started with Rust Cloudflare Workers: What we’re Building #

Often you need to send out an alert from a serverless function when a certain event occurs. To help out we generate a test email message using SendGrid from our worker. Even when you don’t need to send a message from your worker, you will often need to interact with external services (such as databases), making REST calls. We use Reqwest in our serverless function to send the email. So, even if you don’t need to send email in your first Rust Cloudflare worker, you will have some example code for making REST requests.

⚙️ Rust Setup #

You can set up Rust with Homebrew or other package managers. Rust’s recommended approach  though, is to install from the Terminal from their script:

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

This installs rustup which manages your Rust installation. As examples, to update rustup itself and your Rust tooling you would use the following commands:

    
rustup self update
rustup update

Cargo is the Rust package manager. To search for crates (Rust packages) try https://crates.io/  . We won’t go into more detail on rustup and cargo here as you will probably find more complete information wherever you are learning Rust.

🔧 Serverless Rust Cloudflare Worker Setup #

You need a Cloudflare account to start; sign up for Cloudflare  if you don’t yet have one. Next, set up the wrangler CLI tool. You can skip this if you already have wrangler installed globally via npm (for example if you have already worked with JavaScript or TypeScript workers on your system). If you do need to set up wrangler, the easiest way to do so is using Rust’s cargo package manager:

    
cargo install wrangler

This installs the latest available version of wrangler. To update wrangler later, just run the same command. If a newer version is available Cargo will update, otherwise, it just tells you the package is already installed.

Getting Started with Rust Cloudflare Workers: Wrangler Update: screenshot shows Terminal user has typed "cargo install wrangler generate". Response includes "Package already installed"
Getting Started with Rust Cloudflare Workers: Wrangler Update

Note that Cargo installs wrangler to the current user’s profile ( ~/.cargo/bin/wrangler), so you do not need admin privileges. Rustup should have added the ~/.cargo/bin directory to your Terminal PATH environment variable. To check this, type wrangler --version in the Terminal. If all is well you should get the something like wrangler 1.19.11 as output. If you don’t, you might need to add the directory to your PATH manually.

🔨 Generate Rust Cloudflare Worker Skeleton Boilerplate #

Next we use wrangler to create the project boiler plate:

    
wrangler generate --type=rust my-rust-cloudflare-worker
Getting Started with Rust Cloudflare Workers: Wrangler Generate: screenshot shows Terminal user has typed "wrangler generate --type=rust my-rust-cloudflare-worker". Response shows "Done!"
Getting Started with Rust Cloudflare Workers: Wrangler Generate

This creates a wrangler.toml file which contains project config. You can define variables for use in the Rust code here. As an example WORKERS_RS_VERSION is defined in line 7:

wrangler.toml
toml
    
1 name = "my-rust-cloudflare-worker"
2 type = "javascript"
3 workers_dev = true
4 compatibility_date = "2022-04-19"
5
6 [vars]
7 WORKERS_RS_VERSION = "0.0.9"
8
9 [build]
10 command = "cargo install -q worker-build && worker-build --release" # required
11 # TRUNCATED...

Do not store secrets (like API keys) here, we will see how to define those in a moment.

📦 Package Meta #

Another file which wrangler has generated in the project is the usual Cargo.toml files:

Cargo.toml
toml
    
1 [package]
2 name = "my-rust-cloudflare-worker"
3 version = "0.1.0"
4 authors = ["Blake Costa <[email protected]>"]
5 edition = "2018"
6 description = "My first Rust Cloudflare Worker"
7 repository = "https://github.com/example-profile/my-rust-cloudflare-worker"
8 license = "BSD-3-Clause"

Currently wrangler does not automatically include the last three lines shown, but you may like to include them to follow best practice (customising to suit your needs).

🖥 Dev environment #

Fire up the dev environment (with the skeleton code) from the Terminal:

    
wrangler dev
Getting Started with Rust Cloudflare Workers: Wrangler Dev Startup: Terminal shows "Listening on http://127.0.0.1:8787"
Getting Started with Rust Cloudflare Workers: Wrangler Dev Startup

It will take a moment to build the worker the first time you run it. Once the dev environment is ready to receive requests the Terminal will have the message Listening on http://127.0.0.1:8787.

In production, typically the worker will be invoked by a REST GET or PUT request sent to its production address. In development, we can send curl requests to the address above. The boilerplate project includes some code we can use to test the worker. In your browser go to http://127.0.0.1:8787/worker-version alternatively, send a GET request using curl from the Terminal (in a separate tab):

    
curl "http://127.0.0.1:8787/worker-version"
Getting Started with Rust Cloudflare Workers: Wrangler Dev Test: screenshot shows Terminal user has typed "curl "http://127.0.0.1:8787/worker-version"" response is "0.0.9"
Getting Started with Rust Cloudflare Workers: Wrangler Dev Test

If all is well you will get a response with the worker version (this is the value defined in wrangler.toml which we mentioned earlier).

🔌 Connecting your project to your Cloudflare account #

To store a secret API key, we will need to connect our local project to our Cloudflare account. To do this, just type the following command from within the project directory:

    
wrangler login

wrangler will prompt asking if it can open a page in your browser. To proceed accept this and wrangler opens your default browser. You need to log into your Cloudflare account to authorise wrangler. If you prefer to use a browser other than your default browser, this is also possible. Just paste the link wrangler prints in the Terminal from there to your preferred browser. Follow instructions in the Cloudflare console to proceed.

🤫 Getting Started with Rust Cloudflare Workers: Environment Variables #

We will use SendGrid to send a test email in a moment. For this to work, we need to make our SendGrid API key available to the wrangler environment. We can do this from the Terminal:

    
wrangler secret put SENDGRID_APIKEY
Getting Started with Rust Cloudflare Workers: Wrangler Secret: Terminal screenshot: user has entered "wrangler secret put SENDGRID_APIKEY" and Terminal is promting user to enter the secret text which is printed to the Terminal
Getting Started with Rust Cloudflare Workers: Wrangler Secret

Paste in your SendGrid API key when wrangler prompts and it will store it in the Cloudflare environment. You can access the value both in local development and production.

If you are working with a service other than, SendGrid, just change the name to one that makes sense and add any additional secrets your service may require with additional wrangler secret put commands.

✉️ Sending Email with Rust Cloudflare Workers #

As a final step we will add some Rust code to send a test email. We use the SendGrid REST API so you see how to do this. The crate we use for making REST calls is Reqwest. This performs exactly the function we would use axios or node-fetch for in a JavaScript or node environment. We will also use serde (contraction of serialise, deserialise) to create the JSON message data sent to the SendGrid API.

Add the Reqwest and Serde crates in Cargo.toml to make them available to our code:

Cargo.toml
toml
    
16 [dependencies]
17 cfg-if = "0.1.2"
18 reqwest = { version = "0.11.10", features = ["json"]}
19 serde = "1.0.117"
20 worker = "0.0.9"
21 serde_json = "1.0.67"

In a moment we will add a sendgrid_client module. For now let’s just declare it in src/lib.rs:

src/lib.rs
rust
    
1 mod sendgrid_client;
2 use sendgrid_client::{EmailRecipientSender, SendgridClient};
3
4 use serde_json::json;
5 use worker::*;

Then we will add a new route to listen on. The worker will send an email when it receives a GET request on the /test-email endpoint:

src/lib.rs
rust
    
52 .get("/worker-version", |_, ctx| {
53 let version = ctx.var("WORKERS_RS_VERSION")?.to_string();
54 Response::ok(version)
55 })
56 .get_async("/test-email", |_req, ctx| async move {
57 let sendgrid_api_key = ctx.var("SENDGRID_APIKEY")?.to_string();
58 let sendgrid_client = SendgridClient::new(&sendgrid_api_key);
59 sendgrid_client
60 .send_email(
61 EmailRecipientSender { // to
62 email: "[email protected]".to_string(),
63 name: "River Santos".to_string(),
64 },
65 EmailRecipientSender { // from
66 email: "[email protected]".to_string(),
67 name: "Blake Costa".to_string(),
68 },
69 EmailRecipientSender { // reply to
70 email: "[email protected]".to_string(),
71 name: "Blake Costa".to_string(),
72 },
73 "Test message", // subject
74 "This is just a test message", // message
75 )
76 .await;
77 Response::ok("Over and out!")
78 })
79 .run(req, env)
80 .await
81 }

Change the email addresses to ones linked to your SendGrid account and which make sense for you. Notice how you can use the secret we previously defined (line 57).

SendGrid API #

The SendGrid API expects our email in JSON format and we use serde to help us form this. Below is the JSON format SendGrid expects. If you are using a different email service you will need to tinker with the SendGrid Module to make it work.

    
{
"personalizations": [
{
"to": [{ "email": "[email protected]", "name": "John Doe" }],
"subject": "Hello, World!"
}
],
"content": [{ "type": "text/plain", "value": "Heya!" }],
"from": { "email": "[email protected]", "name": "Sam Smith" },
"reply_to": { "email": "[email protected]", "name": "Sam Smith" }
}

SendGrid Module #

Finally add this SendGrid module code to send messages (create the new src/sendgrid_client.rs file):

src/sendgrid_client.rs
rust
    
1 use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
2 use serde::Serialize;
3 use worker::console_log;
4
5 #[derive(Serialize)]
6 pub struct EmailRecipientSender {
7 pub email: String,
8 pub name: String,
9 }
10
11 #[derive(Serialize)]
12 struct EmailPersonalization {
13 to: Vec<EmailRecipientSender>,
14 subject: String,
15 }
16
17 #[derive(Serialize)]
18 struct EmailContent {
19 r#type: String,
20 value: String,
21 }
22
23 #[derive(Serialize)]
24 struct SendGridEmail {
25 personalizations: Vec<EmailPersonalization>,
26 content: Vec<EmailContent>,
27 from: EmailRecipientSender,
28 reply_to: EmailRecipientSender,
29 }
30
31 pub struct SendgridClient {
32 api_key: String,
33 base_url: String,
34 }
35
36 impl SendgridClient {
37 pub fn new(api_key: &str) -> SendgridClient {
38 SendgridClient {
39 api_key: api_key.into(),
40 base_url: "https://api.sendgrid.com/v3/mail/send".to_string(),
41 }
42 }
43
44 pub async fn send_email(
45 &self,
46 to: EmailRecipientSender,
47 from: EmailRecipientSender,
48 reply_to: EmailRecipientSender,
49 subject: &str,
50 message: &str,
51 ) {
52 let client = reqwest::Client::new();
53 let mut headers = HeaderMap::new();
54 let authorisation_header_value = format!("Bearer {}", self.api_key);
55 headers.insert(
56 AUTHORIZATION,
57 HeaderValue::from_str(&authorisation_header_value).unwrap(),
58 );
59 let data: SendGridEmail = SendGridEmail {
60 personalizations: vec![EmailPersonalization {
61 to: vec![to],
62 subject: subject.to_string(),
63 }],
64 content: vec![EmailContent {
65 r#type: "text/plain".to_string(),
66 value: message.to_string(),
67 }],
68 from,
69 reply_to,
70 };
71 match client
72 .post(&self.base_url)
73 .headers(headers)
74 .json(&data)
75 .send()
76 .await
77 {
78 Ok(_response) => {
79 console_log!("Email sent")
80 }
81 Err(error) => {
82 console_log!("Error sending email: {error}")
83 }
84 };
85 }
86 }

With this example, you see a way to send a PUT request with JSON body using Reqwest. We send the API key as a Bearer Authorization header (formed in lines 54 – 58). We could write the JSON body as a raw string, but defining the structs and using serde to serialise adds a little checking.

You can also use serde to deserialise. As an example you could do this if your worker is listening for a PUT request with a JSON body. In this case serde helps us convert the JSON body to Rust structs which we can manipulate in the code.

The SendGrid API expects a type field on content entries. However type is a reserved keyword in Rust so to escape it, in lines 19 & 65, we use r#type. We can use console_log for debugging with Rust Cloudflare Workers like we would use console.log() in JavaScript. We see it in action in lines 79 & 82. For that to work, we need the use directive in line 3.

Reqwest and Serde are very powerful and you will probably use them a lot as you start using Rust Cloudflare Workers. See Reqwest docs  as well as serde docs  for more on how you can use them.

💯 Getting Started with Rust Cloudflare Workers: Testing it Out #

The test we use here is a little rudimentary. You might already know that Rust lets you define unit tests within source files. For testing code which calls external APIs (like the SendGrid call in our code) there are a few crates you can use. I have got on well with the httptest crate in the past. It works with async code and lets you check the data which your code sends and mock the response. We won’t look at it here otherwise the post will get too long. Just wanted to make sure you knew such packages exist!

To test either open https://127.0.0.1:8787/test-email in your browser to use curl (like we did earlier):

    
curl "http://127.0.0.1:8787/test-email"
Getting Started with Rust Cloudflare Workers: Wrangler Test: Terminal screenshot: output shows a request was received by Wrangler dev and the console log message "Email sent."
Getting Started with Rust Cloudflare Workers: Test

Check your inbox. Fingers crossed, you’ll have the test email.

The final step, once you are happy everything is working, is to publish the worker to production:

    
wrangler publish

wrangler will let you know the public url for the worker. Of course, you can also customise this in the Cloudflare console.

🙌🏽 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:

  • a way to send email using a REST API from Rust Cloudflare Workers using Reqwest to POST a JSON body,
  • how to store and make secrets and environment variables accessible to Rust Cloudflare Workers,
  • how you can add debug console_log message to you Cloudflare Worker.

The Getting started with Rust Cloudflare Workers demo code is in the Rodney Lab GitHub repo  .

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: Summary #

How do you spin up a new Rust Cloudflare Workers project? #

Before getting started with Rust Cloudflare Workers, you need to have the Rust toolchain (rustup) set up on your machine. You will also need a Cloudflare account (which is free to sign up for). With those preliminaries out of the way, use the wrangler generate --type=rust my-project-name command from the Terminal. This will initialise your new project. Finally use the cd command to change into the new project directory (created by wrangler) which will match the project name. You can run wrangler dev to spin up a test server. The project will include the wrangler config in wrangler.toml, project config in Cargo.toml and some skeleton Rust code in the src directory.

How do you handle environment variables with Rust Cloudflare workers? #

Cloudflare lets you set up secrets or environment variables which you can use in local development as well as in production. You can set the secrets from the command line using wrangler. For it to work though, you first need to link your Rust Cloudflare workers project to your Cloudflare account. Do this by running wrangler login and pasting the output link into the browser in which you are already logged into your Cloudflare account. This is a piece of one-off setup. Now, any time you want to add a new secret to you Rust Cloudflare Workers project just run the wrangler secret put SECRET_NAME command and then, once prompted, enter a value for the secret. The secret is output to the Terminal as you type it.

How can you send email from a Rust Cloudflare Worker? #

The easiest way to send email from a Rust Cloudflare worker is using a REST API, assuming you have an account with SendGrid or another similar transactional email service. SendGrid and other services have APIs and typically you send a POST request to their endpoint from your worker. We have seen with SendGrid that the data in encoded as a JSON object. We use serde to help form the JSON data object from our Rust code. To make the REST call we used Reqwest. To access these from our Rust Cloudflare worker, we just needed to include them in Cargo.toml like other Rust crates we use in standalone Rust apps. An alternative is to use a Mail Transfer Agent, using TLS so the message is encrypted in transport. The advantage of this approach is that it is easier to switch to a different email service, especially if you have to do so at short notice.

🙏🏽 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 consider supporting me through Buy me a Coffee.

@askRodney avatar

Rodney

@askRodney

Just put together a new post talking about what I learned geting started with Rust Cloudflare Workers.

It includes how you can send email from a Worker using the

@SendGrid REST API with Reqwest.

I hope you find it useful!#askRodneyhttps://t.co/q90C6M226z

— Rodney (@askRodney) April 20, 2022

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 @askRodney on Twitter and also askRodney on Telegram  . Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as SvelteKit. Also subscribe to the newsletter to keep up-to-date with our latest projects.

Thanks for reading this post. I hope you found it valuable. Please get in touch with your feedback and suggestions for posts you would like to see. Read more about me …

Rodney from Rodney Lab
TAGS:
RUSTSERVERLESS

Likes:

Likes

  • Jonathan Giddy profile avatar
  • David Warrington profile avatar
  • matheus profile avatar
Likes provided by Twitter via Webmentions.

Related Posts

Temporal API Cheatsheet: Quick Guide to new JS API

Temporal API Cheatsheet: Quick Guide to new JS API

astro
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Leave a comment …

Your information will be handled in line with our Privacy Policy .

Ask for more

1 Nov 2022 — Astro Server-Side Rendering: Edge Search Site
3 Oct 2022 — Svelte eCommerce Site: SvelteKit Snipcart Storefront
1 Sept 2022 — Get Started with SvelteKit Headless WordPress

Copyright © 2020 – 2023 Rodney Johnson. All Rights Reserved. Please read important copyright and intellectual property information.

  • Home
  • Plus +
  • Newsletter
  • Contact
  • Terms of Use
  • Privacy Policy
We use cookies  to enhance visitors’ experience. Please click the “Options” button to make your choice.  Learn more here.