3

Getting Started with Rust Cloudflare Workers | Rodney Lab

 2 years ago
source link: https://rodneylab.com/getting-started-rust-cloudflare-workers/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
NEXT POST >
LATEST POST >>

Getting Started with Rust Cloudflare Workers #

Updated 3 hours ago
10 minute read Gunning Fog Index: 5.8
Content by Rodney
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?
  • Just starting out,
  • I can get by,
  • I’m quite competent,
  • I’m a beast!
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 Astro: Wrangler Update: screenshot shows Terminal user has typed

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 Astro: Wrangler Generate: screenshot shows Terminal user has typed

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
1 name = "my-rust-cloudflare-worker"
2 type = "javascript"
3 workers_dev = true
4 compatibility_date = "2022-04-19"
6 [vars]
7 WORKERS_RS_VERSION = "0.0.9"
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
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 Astro: Wrangler Dev Startup: Terminal shows

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 Astro: Wrangler Dev Test: screenshot shows Terminal user has typed

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 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 Astro: Wrangler Secret: Terminal screenshot: user has entered

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
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
1 mod sendgrid_client;
2 use sendgrid_client::{EmailRecipientSender, SendgridClient};
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
52 .get("/worker-version", |_, ctx| {
53 let version = ctx.var("WORKERS_RS_VERSION")?.to_string();
54 Response::ok(version)
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(),
65 EmailRecipientSender { // from
66 email: "[email protected]".to_string(),
67 name: "Blake Costa".to_string(),
69 EmailRecipientSender { // reply to
70 email: "[email protected]".to_string(),
71 name: "Blake Costa".to_string(),
73 "Test message", // subject
74 "This is just a test message", // message
76 .await;
77 Response::ok("Over and out!")
79 .run(req, env)
80 .await

Change the email addresses to ones linked to your SendGrid account and which 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 file):

src/sendgrid_client.rs
1 use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
2 use serde::Serialize;
3 use worker::console_log;
5 #[derive(Serialize)]
6 pub struct EmailRecipientSender {
7 pub email: String,
8 pub name: String,
11 #[derive(Serialize)]
12 struct EmailPersonalization {
13 to: Vec<EmailRecipientSender>,
14 subject: String,
17 #[derive(Serialize)]
18 struct EmailContent {
19 r#type: String,
20 value: String,
23 #[derive(Serialize)]
24 struct SendGridEmail {
25 personalizations: Vec<EmailPersonalization>,
26 content: Vec<EmailContent>,
27 from: EmailRecipientSender,
28 reply_to: EmailRecipientSender,
31 pub struct SendgridClient {
32 api_key: String,
33 base_url: String,
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(),
44 pub async fn send_email(
45 &self,
46 to: EmailRecipientSender,
47 from: EmailRecipientSender,
48 reply_to: EmailRecipientSender,
49 subject: &str,
50 message: &str,
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(),
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,
71 match client
72 .post(&self.base_url)
73 .headers(headers)
74 .json(&data)
75 .send()
76 .await
78 Ok(_response) => {
79 console_log!("Email sent")
81 Err(error) => {
82 console_log!("Error sending email: {error}")

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 5458). We could write the JSON body as a raw string, but defining the structs and using serde to Serialize 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 Astro: Wrangler Test: Terminal screenshot: output shows a request was received by Wrangler dev and the console log message

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.

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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK