From b1c17b66011e0ae4b40dd42bf1dbf91ca9238bf3 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 12 Sep 2024 17:11:08 +0200 Subject: [PATCH 1/9] add rate limit tutorial --- .../tutorials/handle-rate-limits/index.mdx | 391 ++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 src/content/docs/queues/tutorials/handle-rate-limits/index.mdx diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx new file mode 100644 index 000000000000000..a493e0e0b6d4907 --- /dev/null +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -0,0 +1,391 @@ +--- +updated: 2024-09-12 +difficulty: Beginner +title: Handle rate limits of external APIs +summary: Example of how to use Queues to handle rate limits of external APIs. +content_type: πŸ“ Tutorial +pcx_content_type: tutorial +products: + - Workers + - Queues +languages: + - TypeScript +sidebar: + order: 1002 +head: + - tag: title + content: Cloudflare Queues - Queues & Rate Limits +description: Example of how to use Queues to handle rate limits of external APIs. +--- + +import { Render, PackageManagers } from "~/components"; + +This tutorial explains how to use Queues to handle rate limits of external APIs. In this tutorial, you will build an application that sends email notifications using [Resend](https://www.resend.com/), but you can use this pattern to handle rate limits of any external API. + +Resend is a service that allows you to send emails from your application. It provides a simple API that you can use to send emails. Resend has a default [rate limit](https://resend.com/docs/api-reference/introduction#rate-limit) of 2 requests per second. You will use Queues to handle the rate limit of Resend. + +## Prerequisites + + + +4. Sign up for Resend and generate an API key by following the instructions on the [Resend documentation](https://resend.com/docs/dashboard/api-keys/introduction). + +### Queues access + +Additionally, you will need access to Queues. + + + +## 1. Create new Workers application + +To get started, create a Worker application using the [`create-cloudflare` CLI](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare). Open a terminal window and run the following command: + + + + + +Then, move into your newly created directory: + +```sh frame="none" +cd resend-rate-limit-queue +``` + +## 2. Set up a Queue + +You need to create a Queue and a binding to your Worker. Run the following command to create a Queue named `rate-limit-queue`. + +```sh title="Create a Queue" +npx wrangler queues create rate-limit-queue +``` + +```txt title="Output" +Creating queue rate-limit-queue. +Created queue rate-limit-queue. +``` + +### Add Queue bindings to wrangler.toml + +Next, in your `wrangler.toml` file, add the following: + +```toml +[[queues.producers]] +binding = "EMAIL_QUEUE" +queue = "rate-limit-queue" + +[[queues.consumers]] +queue = "rate-limit-queue" +max_batch_size = 2 +max_batch_timeout = 10 +max_retries = 3 +``` + +Adding the `max_batch_size` of 2 to the consumer queue is important because the Resend API has a default rate limit of 2 requests per second. This batch size allows the queue to process the message in the batch size of 2. If the batch size is less than 2, the queue will wait for 10 seconds to collect the next message. If no more messages are available, the queue will process the message in the batch. + +Your final `wrangler.toml` file should look similar to the one below. + +```toml title="wrangler.toml" +#:schema node_modules/wrangler/config-schema.json +name = "resend-rate-limit-queue" +main = "src/index.ts" +compatibility_date = "2024-09-09" +compatibility_flags = ["nodejs_compat"] + +[[queues.producers]] +binding = "EMAIL_QUEUE" +queue = "rate-limit-queue" + +[[queues.consumers]] +queue = "rate-limit-queue" +max_batch_size = 2 +max_batch_timeout = 10 +max_retries = 3 +``` + +## 3. Add bindings to environment + +Add the bindings to the environment interface in `worker-configuration.d.ts`, so TypeScript correctly types the bindings. Type the queue as `Queue`. The following step will show you how to change this type. + +```ts title="worker-configuration.d.ts" +interface Env { + EMAIL_QUEUE: Queue; +} +``` + +## 4. Send message to the queue + +The application will send a message to the queue when this worker receives a request. For simplicity, you will send the email address as a message to the queue. A new message will be sent to the queue with a delay of 1 second. + +```ts title="src/index.ts" +export default { + async fetch(req: Request, env: Env): Promise { + try { + await env.EMAIL_QUEUE.send( + { email: await req.text() }, + { delaySeconds: 1 }, + ); + return new Response("Success!"); + } catch (e) { + return new Response("Error!", { status: 500 }); + } + }, +}; +``` + +This will accept requests to any subpath and forwards the request's body. It expects that the request body only contains an email. In production, you should check that the request was a `POST` request. Moreover, you should avoid sending such sensitive information (email) directly to the queue. Instead, you can send a message to the queue that contains a unique identifier for the user. Then, your consumer queue can use the unique identifier to look up the email address in a database and use that to send the email. + +## 5. Process the messages in the queue + +After the message is sent to the queue, it will be processed by the consumer queue. The consumer queue will process the message and send the email. Add the `queue()` handler as shown below. + +```ts title="src/index.ts" ins={1-3,17-28} +interface Message { + email: string; +} + +export default { + async fetch(req: Request, env: Env): Promise { + try { + await env.EMAIL_QUEUE.send( + { email: await req.text() }, + { delaySeconds: 1 }, + ); + return new Response("Success!"); + } catch (e) { + return new Response("Error!", { status: 500 }); + } + }, + async queue(batch: MessageBatch, env: Env): Promise { + for (const message of batch.messages) { + try { + console.log(message.body.email); + // send email + message.ack(); + } catch (e) { + console.error(e); + message.retry({ delaySeconds: 5 }); + } + } + }, +}; +``` + +The above `queue()` handler will log the email address to the console and send the email. It will also retry the message if the email sending fails. The `delaySeconds` is set to 5 seconds to avoid sending the email too quickly. + +To test the application, run the following command. + +```sh title="Start the development server" +npm run dev +``` + +Use the following cURL command to send a request to the application. + +```sh title="Test with a cURL request" +curl -X POST -d "test@example.com" http://localhost:8787/ +``` + +On success, you should see the following output in the console. + +```txt title="Output" +[wrangler:inf] POST / 200 OK (2ms) +QueueMessage { + attempts: 1, + body: { email: 'test@example.com' }, + timestamp: 2024-09-12T13:48:07.236Z, + id: '72a25ff18dd441f5acb6086b9ce87c8c' +} +``` + +## 6. Set up Resend + +To call the Resend API, you need to configure the Resend API key. Create a `.dev.vars` file in the root of your project and add the following: + +```txt title=".dev.vars" +RESEND_API_KEY='your-resend-api-key' +``` + +Replace `your-resend-api-key` with your actual Resend API key. + +Next, update the `Env` interface in `worker-configuration.d.ts` to include the `RESEND_API_KEY` variable. + +```ts title="worker-configuration.d.ts" ins={3} +interface Env { + EMAIL_QUEUE: Queue; + RESEND_API_KEY: string; +} +``` + +Lastly, install the [`resend` package](https://www.npmjs.com/package/resend) using the following command + +```sh title="Install Resend" +npm install resend +``` + +You can now use the `RESEND_API_KEY` variable in your code. + +## 7. Send email with Resend + +In your `src/index.ts` file, import the Resend package and update the `queue()` handler to send the email. + +```ts title="src/index.ts" ins={1,21,26-40} del={24,41} +import { Resend } from "resend"; + +interface Message { + email: string; +} + +export default { + async fetch(req: Request, env: Env): Promise { + try { + await env.EMAIL_QUEUE.send( + { email: await req.text() }, + { delaySeconds: 1 }, + ); + return new Response("Success!"); + } catch (e) { + return new Response("Error!", { status: 500 }); + } + }, + async queue(batch: MessageBatch, env: Env): Promise { + // Initialize Resend + const resend = new Resend(env.RESEND_API_KEY); + for (const message of batch.messages) { + try { + console.log(message.body.email); + // send email + const sendEmail = await resend.emails.send({ + from: "onboarding@resend.dev", + to: [message.body.email], + subject: "Hello World", + html: "Sending an email from Worker!", + }); + + // check if the email failed + if (sendEmail.error) { + console.error(sendEmail.error); + message.retry({ delaySeconds: 5 }); + } else { + // if success, ack the message + message.ack(); + } + message.ack(); + } catch (e) { + console.error(e); + message.retry({ delaySeconds: 5 }); + } + } + }, +}; +``` + +The `queue()` handler will now send the email using the Resend API. It also checks if the email sending failed and retries the message if it did. + +The final script is included below. + +```ts title="src/index.ts" +import { Resend } from "resend"; + +interface Message { + email: string; +} + +export default { + async fetch(req: Request, env: Env): Promise { + try { + await env.EMAIL_QUEUE.send( + { email: await req.text() }, + { delaySeconds: 1 }, + ); + return new Response("Success!"); + } catch (e) { + return new Response("Error!", { status: 500 }); + } + }, + async queue(batch: MessageBatch, env: Env): Promise { + // Initialize Resend + const resend = new Resend(env.RESEND_API_KEY); + for (const message of batch.messages) { + try { + // send email + const sendEmail = await resend.emails.send({ + from: "onboarding@resend.dev", + to: [message.body.email], + subject: "Hello World", + html: "Sending an email from Worker!", + }); + + // check if the email failed + if (sendEmail.error) { + console.error(sendEmail.error); + message.retry({ delaySeconds: 5 }); + } else { + // if success, ack the message + message.ack(); + } + } catch (e) { + console.error(e); + message.retry({ delaySeconds: 5 }); + } + } + }, +}; +``` + +To test the application, start the development server using the following command. + +```sh title="Start the development server" +npm run dev +``` + +Use the following cURL command to send a request to the application. + +```sh title="Test with a cURL request" +curl -X POST -d "delivered@resend.dev" http://localhost:8787/ +``` + +On the Resend dashboard, you should see that the email was sent to the provided email address. + +## 8. Deploy your Worker + +To deploy your Worker, run the following command: + +```sh title="Deploy your Worker" +npx wrangler deploy +``` + +Lastly, add the Resend API key using the following command. + +```sh title="Add the Resend API key" +npx wrangler secret put RESEND_API_KEY +``` + +Enter the value of your API key. Your API key will get added to your project. You can now use the `RESEND_API_KEY` variable in your code. + +You have successfully created a Worker which can send emails using the Resend API respecting rate limits. + +To test your Worker, you could use the following cURL request. Replace `` with the URL of your deployed Worker. + +```bash title="Test with a cURL request" +curl -X POST -d "delivered@resend.dev" +``` + +Refer to the [GitHub repository for the complete tutorial](https://github.com/cloudflare/queues-rate-limit). If you are using [Hono](https://hono.dev/), you refer to the [Hono example](https://github.com/harshil1712/resend-rate-limit-demo). + +## Related resources + +- [How Queues works](/queues/reference/how-queues-works/) +- [Queues Batching and Retries](/queues/configuration/batching-retries/) +- [Resend](https://resend.com/docs/) From e123e64352f0977f92406a6fb8070c6c31fff30f Mon Sep 17 00:00:00 2001 From: Harshil Agrawal <18901032+harshil1712@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:16:11 +0200 Subject: [PATCH 2/9] Update src/content/docs/queues/tutorials/handle-rate-limits/index.mdx Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com> --- src/content/docs/queues/tutorials/handle-rate-limits/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index a493e0e0b6d4907..d06c2608019cae3 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -127,7 +127,7 @@ interface Env { ## 4. Send message to the queue -The application will send a message to the queue when this worker receives a request. For simplicity, you will send the email address as a message to the queue. A new message will be sent to the queue with a delay of 1 second. +The application will send a message to the queue when this Worker receives a request. For simplicity, you will send the email address as a message to the queue. A new message will be sent to the queue with a delay of 1 second. ```ts title="src/index.ts" export default { From 645183c523f394c6cfcb2301c63879af297ec5f7 Mon Sep 17 00:00:00 2001 From: kodster28 Date: Thu, 12 Sep 2024 12:28:06 -0500 Subject: [PATCH 3/9] [Hyperlint] Update checks to be more forgiving for Wrangler capitalization + add new check + remove logic around backticks --- .github/styles/cloudflare/NonStandardQuotes.yml | 14 ++++++++++++++ .../config/vocabularies/cloudflare/accept.txt | 2 +- .hyperlint/style_guide_test.md | 8 +++----- .vale.ini | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 .github/styles/cloudflare/NonStandardQuotes.yml diff --git a/.github/styles/cloudflare/NonStandardQuotes.yml b/.github/styles/cloudflare/NonStandardQuotes.yml new file mode 100644 index 000000000000000..36a76b8006cf0ce --- /dev/null +++ b/.github/styles/cloudflare/NonStandardQuotes.yml @@ -0,0 +1,14 @@ +--- +# Warning: cloudflare.NonStandardQuotes +# +# Use only standard single and double quotes, not left or right quotes. +# +# For a list of all options, see https://vale.sh/docs/topics/styles/ +extends: existence +message: "Use standard single quotes or double quotes only. Do not use left or right quotes." +level: warning +ignorecase: true +link: https://developers.cloudflare.com/style-guide/grammar/punctuation-marks-and-symbols/quotation-marks/ +scope: raw +raw: + - "[β€˜β€™β€œβ€]" diff --git a/.github/styles/config/vocabularies/cloudflare/accept.txt b/.github/styles/config/vocabularies/cloudflare/accept.txt index 874d4b12636ab46..7b20399e3543ade 100644 --- a/.github/styles/config/vocabularies/cloudflare/accept.txt +++ b/.github/styles/config/vocabularies/cloudflare/accept.txt @@ -36,7 +36,7 @@ Wi-Fi WordPress Worker Workers -Wrangler +[wW]rangler VMware VPN YubiKey diff --git a/.hyperlint/style_guide_test.md b/.hyperlint/style_guide_test.md index fd25247eaf8d7b9..35d353eebf0abb0 100644 --- a/.hyperlint/style_guide_test.md +++ b/.hyperlint/style_guide_test.md @@ -1,9 +1,7 @@ # This is a test -Bad link at [this page](/test/). +testing `worker-configuration.d.ts`. -Build a better internet. +Then also, β€˜this thing' -Testing [this page](https://developers.cloudflare.com/test/). - -Going to [the marketing site](https://www.cloudflare.com) +What about wrangler or Wrangler. diff --git a/.vale.ini b/.vale.ini index 901492fa0f8b383..7753fd439c403a4 100644 --- a/.vale.ini +++ b/.vale.ini @@ -10,7 +10,7 @@ mdx = md [*] # Ignore code surrounded by backticks or plus sign, URLs, parameters defaults, and angle brackets. -TokenIgnores = (<\/?[A-Z].+>), (\x60[^\n\x60]+\x60), ([^\n]+=[^\n]*), (\+[^\n]+\+), (http[^\n]+\[), (https[^\n]+\[), ([^\n]+@[^\n]+\.[^\n]) +TokenIgnores = (<\/?[A-Z].+>), ([^\n]+=[^\n]*), (\+[^\n]+\+), (http[^\n]+\[), (https[^\n]+\[), ([^\n]+@[^\n]+\.[^\n]) [*.md] BasedOnStyles = Vale, cloudflare From 903342bbfa285e7e325a423a0653b926ef596af5 Mon Sep 17 00:00:00 2001 From: kodster28 Date: Thu, 12 Sep 2024 12:29:43 -0500 Subject: [PATCH 4/9] Revert "[Hyperlint] Update checks to be more forgiving for Wrangler capitalization + add new check + remove logic around backticks" This reverts commit 645183c523f394c6cfcb2301c63879af297ec5f7. --- .github/styles/cloudflare/NonStandardQuotes.yml | 14 -------------- .../config/vocabularies/cloudflare/accept.txt | 2 +- .hyperlint/style_guide_test.md | 8 +++++--- .vale.ini | 2 +- 4 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 .github/styles/cloudflare/NonStandardQuotes.yml diff --git a/.github/styles/cloudflare/NonStandardQuotes.yml b/.github/styles/cloudflare/NonStandardQuotes.yml deleted file mode 100644 index 36a76b8006cf0ce..000000000000000 --- a/.github/styles/cloudflare/NonStandardQuotes.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# Warning: cloudflare.NonStandardQuotes -# -# Use only standard single and double quotes, not left or right quotes. -# -# For a list of all options, see https://vale.sh/docs/topics/styles/ -extends: existence -message: "Use standard single quotes or double quotes only. Do not use left or right quotes." -level: warning -ignorecase: true -link: https://developers.cloudflare.com/style-guide/grammar/punctuation-marks-and-symbols/quotation-marks/ -scope: raw -raw: - - "[β€˜β€™β€œβ€]" diff --git a/.github/styles/config/vocabularies/cloudflare/accept.txt b/.github/styles/config/vocabularies/cloudflare/accept.txt index 7b20399e3543ade..874d4b12636ab46 100644 --- a/.github/styles/config/vocabularies/cloudflare/accept.txt +++ b/.github/styles/config/vocabularies/cloudflare/accept.txt @@ -36,7 +36,7 @@ Wi-Fi WordPress Worker Workers -[wW]rangler +Wrangler VMware VPN YubiKey diff --git a/.hyperlint/style_guide_test.md b/.hyperlint/style_guide_test.md index 35d353eebf0abb0..fd25247eaf8d7b9 100644 --- a/.hyperlint/style_guide_test.md +++ b/.hyperlint/style_guide_test.md @@ -1,7 +1,9 @@ # This is a test -testing `worker-configuration.d.ts`. +Bad link at [this page](/test/). -Then also, β€˜this thing' +Build a better internet. -What about wrangler or Wrangler. +Testing [this page](https://developers.cloudflare.com/test/). + +Going to [the marketing site](https://www.cloudflare.com) diff --git a/.vale.ini b/.vale.ini index 7753fd439c403a4..901492fa0f8b383 100644 --- a/.vale.ini +++ b/.vale.ini @@ -10,7 +10,7 @@ mdx = md [*] # Ignore code surrounded by backticks or plus sign, URLs, parameters defaults, and angle brackets. -TokenIgnores = (<\/?[A-Z].+>), ([^\n]+=[^\n]*), (\+[^\n]+\+), (http[^\n]+\[), (https[^\n]+\[), ([^\n]+@[^\n]+\.[^\n]) +TokenIgnores = (<\/?[A-Z].+>), (\x60[^\n\x60]+\x60), ([^\n]+=[^\n]*), (\+[^\n]+\+), (http[^\n]+\[), (https[^\n]+\[), ([^\n]+@[^\n]+\.[^\n]) [*.md] BasedOnStyles = Vale, cloudflare From 6302bd0cab04a2d06209ac4ae3bc4a0acc63e5b9 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 19 Sep 2024 15:42:10 +0200 Subject: [PATCH 5/9] update queues resend tutorial --- .../docs/queues/tutorials/handle-rate-limits/index.mdx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index d06c2608019cae3..e61571cf484f0d9 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -93,7 +93,7 @@ max_batch_timeout = 10 max_retries = 3 ``` -Adding the `max_batch_size` of 2 to the consumer queue is important because the Resend API has a default rate limit of 2 requests per second. This batch size allows the queue to process the message in the batch size of 2. If the batch size is less than 2, the queue will wait for 10 seconds to collect the next message. If no more messages are available, the queue will process the message in the batch. +Adding the `max_batch_size` of 2 to the consumer queue is important because the Resend API has a default rate limit of 2 requests per second. This batch size allows the queue to process the message in the batch size of 2. If the batch size is less than 2, the queue will wait for 10 seconds to collect the next message. If no more messages are available, the queue will process the message in the batch. For more information, refer to the [Batching, Retries and Delays documentation](/queues/configuration/batching-retries) Your final `wrangler.toml` file should look similar to the one below. @@ -149,7 +149,11 @@ This will accept requests to any subpath and forwards the request's body. It exp ## 5. Process the messages in the queue -After the message is sent to the queue, it will be processed by the consumer queue. The consumer queue will process the message and send the email. Add the `queue()` handler as shown below. +After the message is sent to the queue, it will be processed by the consumer Worker. The consumer Worker will process the message and send the email. + +Since you have not configured Resend yet, you will log the message to the console. After you configure Resend, you will use it to send the email. + +Add the `queue()` handler as shown below. ```ts title="src/index.ts" ins={1-3,17-28} interface Message { @@ -172,7 +176,7 @@ export default { for (const message of batch.messages) { try { console.log(message.body.email); - // send email + // After configuring Resend, you can send email message.ack(); } catch (e) { console.error(e); From 00297adef0453fb93b87d05554432ef706897890 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Wed, 25 Sep 2024 22:57:05 +0100 Subject: [PATCH 6/9] update repo link and minor changes --- .../docs/queues/tutorials/handle-rate-limits/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index e61571cf484f0d9..774149367f57076 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -1,5 +1,5 @@ --- -updated: 2024-09-12 +updated: 2024-09-25 difficulty: Beginner title: Handle rate limits of external APIs summary: Example of how to use Queues to handle rate limits of external APIs. @@ -32,7 +32,7 @@ Resend is a service that allows you to send emails from your application. It pro ### Queues access -Additionally, you will need access to Queues. +Additionally, you will need access to Cloudflare Queues. @@ -386,7 +386,7 @@ To test your Worker, you could use the following cURL request. Replace ` ``` -Refer to the [GitHub repository for the complete tutorial](https://github.com/cloudflare/queues-rate-limit). If you are using [Hono](https://hono.dev/), you refer to the [Hono example](https://github.com/harshil1712/resend-rate-limit-demo). +Refer to the [GitHub repository](https://github.com/harshil1712/queues-rate-limit) for the complete code for this tutorial. If you are using [Hono](https://hono.dev/), you can refer to the [Hono example](https://github.com/harshil1712/resend-rate-limit-demo). ## Related resources From 7db30e48840d87d41b6e708d57e88881e9bbd813 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 26 Sep 2024 11:00:08 +0100 Subject: [PATCH 7/9] minor fixes --- .../tutorials/handle-rate-limits/index.mdx | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index 774149367f57076..e3995fb168ba23a 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -20,23 +20,21 @@ description: Example of how to use Queues to handle rate limits of external APIs import { Render, PackageManagers } from "~/components"; -This tutorial explains how to use Queues to handle rate limits of external APIs. In this tutorial, you will build an application that sends email notifications using [Resend](https://www.resend.com/), but you can use this pattern to handle rate limits of any external API. +This tutorial explains how to use Queues to handle rate limits of external APIs by building an application that sends email notifications using [Resend](https://www.resend.com/). However, you can use this pattern to handle rate limits of any external API. -Resend is a service that allows you to send emails from your application. It provides a simple API that you can use to send emails. Resend has a default [rate limit](https://resend.com/docs/api-reference/introduction#rate-limit) of 2 requests per second. You will use Queues to handle the rate limit of Resend. +Resend is a service that allows you to send emails from your application via an API. Resend has a default [rate limit](https://resend.com/docs/api-reference/introduction#rate-limit) of two requests per second. You will use Queues to handle the rate limit of Resend. ## Prerequisites -4. Sign up for Resend and generate an API key by following the instructions on the [Resend documentation](https://resend.com/docs/dashboard/api-keys/introduction). +4. Sign up for [Resend](https://resend.com/) and generate an API key by following the guide on the [Resend documentation](https://resend.com/docs/dashboard/api-keys/introduction). -### Queues access - -Additionally, you will need access to Cloudflare Queues. +5. Additionally, you will need access to Cloudflare Queues. -## 1. Create new Workers application +## 1. Create a new Workers application To get started, create a Worker application using the [`create-cloudflare` CLI](https://github.com/cloudflare/workers-sdk/tree/main/packages/create-cloudflare). Open a terminal window and run the following command: @@ -58,7 +56,7 @@ To get started, create a Worker application using the [`create-cloudflare` CLI]( }} /> -Then, move into your newly created directory: +Then, go to your newly created directory: ```sh frame="none" cd resend-rate-limit-queue @@ -66,20 +64,20 @@ cd resend-rate-limit-queue ## 2. Set up a Queue -You need to create a Queue and a binding to your Worker. Run the following command to create a Queue named `rate-limit-queue`. +You need to create a Queue and a binding to your Worker. Run the following command to create a Queue named `rate-limit-queue`: ```sh title="Create a Queue" npx wrangler queues create rate-limit-queue ``` -```txt title="Output" +```sh output Creating queue rate-limit-queue. Created queue rate-limit-queue. ``` -### Add Queue bindings to wrangler.toml +### Add Queue bindings to `wrangler.toml` -Next, in your `wrangler.toml` file, add the following: +In your `wrangler.toml` file, add the following: ```toml [[queues.producers]] @@ -93,9 +91,9 @@ max_batch_timeout = 10 max_retries = 3 ``` -Adding the `max_batch_size` of 2 to the consumer queue is important because the Resend API has a default rate limit of 2 requests per second. This batch size allows the queue to process the message in the batch size of 2. If the batch size is less than 2, the queue will wait for 10 seconds to collect the next message. If no more messages are available, the queue will process the message in the batch. For more information, refer to the [Batching, Retries and Delays documentation](/queues/configuration/batching-retries) +It is important to include the `max_batch_size` of two to the consumer queue is important because the Resend API has a default rate limit of two requests per second. This batch size allows the queue to process the message in the batch size of two. If the batch size is less than two, the queue will wait for 10 seconds to collect the next message. If no more messages are available, the queue will process the message in the batch. For more information, refer to the [Batching, Retries and Delays documentation](/queues/configuration/batching-retries) -Your final `wrangler.toml` file should look similar to the one below. +Your final `wrangler.toml` file should look similar to the example below. ```toml title="wrangler.toml" #:schema node_modules/wrangler/config-schema.json @@ -117,7 +115,7 @@ max_retries = 3 ## 3. Add bindings to environment -Add the bindings to the environment interface in `worker-configuration.d.ts`, so TypeScript correctly types the bindings. Type the queue as `Queue`. The following step will show you how to change this type. +Add the bindings to the environment interface in `worker-configuration.d.ts`, so TypeScript correctly types the bindings. Type the queue as `Queue`. Refer to the following step for instructions on how to change this type. ```ts title="worker-configuration.d.ts" interface Env { @@ -127,7 +125,7 @@ interface Env { ## 4. Send message to the queue -The application will send a message to the queue when this Worker receives a request. For simplicity, you will send the email address as a message to the queue. A new message will be sent to the queue with a delay of 1 second. +The application will send a message to the queue when the Worker receives a request. For simplicity, you will send the email address as a message to the queue. A new message will be sent to the queue with a delay of one second. ```ts title="src/index.ts" export default { @@ -145,7 +143,7 @@ export default { }; ``` -This will accept requests to any subpath and forwards the request's body. It expects that the request body only contains an email. In production, you should check that the request was a `POST` request. Moreover, you should avoid sending such sensitive information (email) directly to the queue. Instead, you can send a message to the queue that contains a unique identifier for the user. Then, your consumer queue can use the unique identifier to look up the email address in a database and use that to send the email. +This will accept requests to any subpath and forwards the request's body. It expects that the request body to contain only an email. In production, you should check that the request was a `POST` request. You should also avoid sending such sensitive information (email) directly to the queue. Instead, you can send a message to the queue that contains a unique identifier for the user. Then, your consumer queue can use the unique identifier to look up the email address in a database and use that to send the email. ## 5. Process the messages in the queue @@ -153,7 +151,7 @@ After the message is sent to the queue, it will be processed by the consumer Wor Since you have not configured Resend yet, you will log the message to the console. After you configure Resend, you will use it to send the email. -Add the `queue()` handler as shown below. +Add the `queue()` handler as shown below: ```ts title="src/index.ts" ins={1-3,17-28} interface Message { @@ -187,23 +185,21 @@ export default { }; ``` -The above `queue()` handler will log the email address to the console and send the email. It will also retry the message if the email sending fails. The `delaySeconds` is set to 5 seconds to avoid sending the email too quickly. +The above `queue()` handler will log the email address to the console and send the email. It will also retry the message if sending the email fails. The `delaySeconds` is set to five seconds to avoid sending the email too quickly. -To test the application, run the following command. +To test the application, run the following command: ```sh title="Start the development server" npm run dev ``` -Use the following cURL command to send a request to the application. +Use the following cURL command to send a request to the application: ```sh title="Test with a cURL request" curl -X POST -d "test@example.com" http://localhost:8787/ ``` -On success, you should see the following output in the console. - -```txt title="Output" +```sh output [wrangler:inf] POST / 200 OK (2ms) QueueMessage { attempts: 1, @@ -232,7 +228,7 @@ interface Env { } ``` -Lastly, install the [`resend` package](https://www.npmjs.com/package/resend) using the following command +Lastly, install the [`resend` package](https://www.npmjs.com/package/resend) using the following command: ```sh title="Install Resend" npm install resend @@ -295,9 +291,9 @@ export default { }; ``` -The `queue()` handler will now send the email using the Resend API. It also checks if the email sending failed and retries the message if it did. +The `queue()` handler will now send the email using the Resend API. It also checks if sending the email failed and will retry the message. -The final script is included below. +The final script is included below: ```ts title="src/index.ts" import { Resend } from "resend"; @@ -348,13 +344,13 @@ export default { }; ``` -To test the application, start the development server using the following command. +To test the application, start the development server using the following command: ```sh title="Start the development server" npm run dev ``` -Use the following cURL command to send a request to the application. +Use the following cURL command to send a request to the application: ```sh title="Test with a cURL request" curl -X POST -d "delivered@resend.dev" http://localhost:8787/ @@ -370,7 +366,7 @@ To deploy your Worker, run the following command: npx wrangler deploy ``` -Lastly, add the Resend API key using the following command. +Lastly, add the Resend API key using the following command: ```sh title="Add the Resend API key" npx wrangler secret put RESEND_API_KEY From 1ec3fb5180e62f6d20ba85f6211b136d1158a7a3 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 26 Sep 2024 12:50:02 +0100 Subject: [PATCH 8/9] update Render componenet params --- .../docs/queues/tutorials/handle-rate-limits/index.mdx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index e3995fb168ba23a..7c651223b32d265 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -48,11 +48,9 @@ To get started, create a Worker application using the [`create-cloudflare` CLI]( file="c3-post-run-steps" product="workers" params={{ - one: "Hello World example", - two: "Hello World Worker", - three: "TypeScript", - four: "Yes", - five: "No", + category: "Hello World example", + type: "Hello World Worker", + lang: "TypeScript", }} /> From 134d9b1209f75e827067b0a1bdf5a056596629e2 Mon Sep 17 00:00:00 2001 From: Harshil Agrawal Date: Thu, 26 Sep 2024 13:05:00 +0100 Subject: [PATCH 9/9] update the category --- src/content/docs/queues/tutorials/handle-rate-limits/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx index 7c651223b32d265..d7b82384ac54f16 100644 --- a/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx +++ b/src/content/docs/queues/tutorials/handle-rate-limits/index.mdx @@ -48,7 +48,7 @@ To get started, create a Worker application using the [`create-cloudflare` CLI]( file="c3-post-run-steps" product="workers" params={{ - category: "Hello World example", + category: "hello-world", type: "Hello World Worker", lang: "TypeScript", }}