INTEGRITY Dokumentace

Řešení rate limitů externích API

Tento tutoriál vysvětluje, jak pomocí Queues řešit omezení rychlosti externích API při tvorbě aplikace, která odesílá e-mailová oznámení pomocí Resend ↗. Tento postup ale můžete použít i ke zvládnutí limitů rychlosti u libovolného externího API.

Resend je služba, která umožňuje odesílat e-maily z vaší aplikace prostřednictvím API. Resend má výchozí limit frekvence požadavků ↗ dva požadavky za sekundu. Queues použijete ke zpracování tohoto limitu rychlosti u Resend.

Předpoklady

  1. Zaregistrujte si účet Cloudflare ↗.
  2. Nainstalujte Node.js ↗.

Správce verzí Node.js

Použijte správce verzí Node, jako je Volta ↗ nebo nvm ↗ abyste se vyhnuli problémům s oprávněními a mohli měnit verze Node.js. Wrangler, o kterém se dozvíte dále v této příručce, vyžaduje verzi Node 16.17.0 nebo novější.

  1. Zaregistrujte se k Resend ↗ a vygenerovat klíč API podle postupu v Dokumentace Resend ↗.

  2. Dále budete potřebovat přístup ke Cloudflare Queues.

Queues jsou zahrnuty v měsíčním poplatku za plán Workers Paid a účtují se podle operací provedených s vašimi frontami. Omezená verze Queues je k dispozici i v plánu Workers Free. Další informace najdete v Ceny pro další podrobnosti.

Než budete moci Queues používat, musíte je aktivovat přes Cloudflare dashboard ↗. K aktivaci Queues potřebujete plán Workers Paid.

Jak povolit Queues:

  1. V dashboardu Cloudflare přejděte na Queues stránce.

    Přejděte na Queues ↗
  2. Vyberte Povolit Queues.

1. Vytvořte novou aplikaci Workers

Pro začátek vytvořte aplikaci Worker pomocí create-cloudflare CLI ↗. Otevřete okno terminálu a spusťte následující příkaz:

npm create cloudflare@latest -- resend-rate-limit-queue

Při nastavení vyberte následující možnosti:

Poté přejděte do nově vytvořeného adresáře:

cd resend-rate-limit-queue

2. Nastavte frontu

Je potřeba vytvořit frontu a vazbu (binding) k Workeru. Následujícím příkazem vytvořte frontu s názvem rate-limit-queue:

Vytvořte frontu
npx wrangler queues create rate-limit-queue
Creating queue rate-limit-queue.
Created queue rate-limit-queue.

Přidejte vazby na frontu do vašeho Konfigurační soubor Wrangler

Do souboru Wrangler přidejte následující:

{
	"queues": {
		"producers": [
			{
				"binding": "EMAIL_QUEUE",
				"queue": "rate-limit-queue"
			}
		],
		"consumers": [
			{
				"queue": "rate-limit-queue",
				"max_batch_size": 2,
				"max_batch_timeout": 10,
				"max_retries": 3
			}
		]
	}
}
[[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

Je důležité uvést max_batch_size dva pro spotřebitelskou frontu je důležité, protože API Resend má výchozí limit rychlosti dva požadavky za sekundu. Tato velikost dávky umožňuje frontě zpracovávat zprávy v dávkách o velikosti dva. Pokud je velikost dávky menší než dva, fronta počká 10 sekund, než shromáždí další zprávu. Pokud již žádné další zprávy nejsou k dispozici, fronta zprávu v dávce zpracuje. Další informace najdete v Dokumentace k dávkování, opakování a zpožděním

Výsledný soubor Wrangler by měl vypadat podobně jako v příkladu níže.

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "resend-rate-limit-queue",
	"main": "src/index.ts",
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"compatibility_flags": [
		"nodejs_compat"
	],
	"queues": {
		"producers": [
			{
				"binding": "EMAIL_QUEUE",
				"queue": "rate-limit-queue"
			}
		],
		"consumers": [
			{
				"queue": "rate-limit-queue",
				"max_batch_size": 2,
				"max_batch_timeout": 10,
				"max_retries": 3
			}
		]
	}
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "resend-rate-limit-queue"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
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. Přidejte vazby do prostředí

Přidejte vazby do rozhraní prostředí v worker-configuration.d.ts, aby TypeScript správně typoval vazby. Fronta má typ Queue<Message>, kde Message je definován v následujícím kroku.

worker-configuration.d.ts
interface Env {
	EMAIL_QUEUE: Queue<Message>;
}

4. Odešlete zprávu do fronty

Aplikace odešle zprávu do fronty ve chvíli, kdy Worker přijme požadavek. Pro zjednodušení pošlete jako zprávu do fronty rovnou e-mailovou adresu. Nová zpráva se do fronty odešle se zpožděním jedné sekundy.

src/index.ts
export default {
	async fetch(req, env, ctx): Promise<Response> {
		try {
			await env.EMAIL_QUEUE.send(
				{ email: await req.text() },
				{ delaySeconds: 1 },
			);
			return new Response("Success!");
		} catch (e) {
			return new Response("Error!", { status: 500 });
		}
	},
} satisfies ExportedHandler<Env>;

Tímto se budou přijímat požadavky na libovolnou podcestu a tělo požadavku se předá dál. Očekává se, že tělo požadavku obsahuje pouze e-mailovou adresu. V produkčním prostředí byste měli ověřit, že požadavek byl typu POST požadavek. Měli byste se také vyvarovat přímého odesílání takto citlivých informací (e-mailu) do fronty. Místo toho můžete do fronty odeslat zprávu obsahující jedinečný identifikátor uživatele. Vaše spotřebitelská fronta pak může tento identifikátor použít k vyhledání e-mailové adresy v databázi a k jejímu odeslání.

5. Zpracujte zprávy ve frontě

Jakmile je zpráva odeslána do fronty, zpracuje ji Worker konzumenta. Ten zprávu zpracuje a odešle e-mail.

Protože jste zatím nenakonfigurovali Resend, zpráva se zapíše do konzole. Jakmile Resend nakonfigurujete, použijete ho k odeslání e-mailu.

Přidejte queue() handler, jak je uvedeno níže:

src/index.ts
interface Message {
	email: string;
}

export default {
	async fetch(req, env, ctx): Promise<Response> {
		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, env, ctx): Promise<void> {
		for (const message of batch.messages) {
			try {
				console.log(message.body.email);
				// After configuring Resend, you can send email
				message.ack();
			} catch (e) {
				console.error(e);
				message.retry({ delaySeconds: 5 });
			}
		}
	},
} satisfies ExportedHandler<Env, Message>;

Výše uvedený queue() handler zaznamená e-mailovou adresu do konzole a odešle e-mail. Pokud se odeslání e-mailu nezdaří, zprávu rovněž zopakuje. Handler delaySeconds je nastaven na pět sekund, aby se zabránilo příliš rychlému odeslání e-mailu.

Chcete-li aplikaci otestovat, spusťte následující příkaz:

Spuštění vývojového serveru
npm run dev

Pomocí následujícího příkazu cURL odešlete požadavek do aplikace:

Otestujte pomocí požadavku cURL
curl -X POST -d "[email protected]" http://localhost:8787/
[wrangler:inf] POST / 200 OK (2ms)
QueueMessage {
  attempts: 1,
  body: { email: '[email protected]' },
  timestamp: 2024-09-12T13:48:07.236Z,
  id: '72a25ff18dd441f5acb6086b9ce87c8c'
}

6. Nastavte Resend

Chcete-li volat rozhraní Resend API, musíte nakonfigurovat klíč Resend API. Vytvořte .dev.vars soubor v kořenovém adresáři projektu a přidejte následující:

.dev.vars
RESEND_API_KEY='your-resend-api-key'

Nahraďte your-resend-api-key svým skutečným API klíčem Resend.

Dále aktualizujte Env rozhraní v worker-configuration.d.ts a zahrňte RESEND_API_KEY proměnná.

worker-configuration.d.ts
interface Env {
	EMAIL_QUEUE: Queue<Message>;
	RESEND_API_KEY: string;
}

Nakonec nainstalujte resend balíček ↗ pomocí následujícího příkazu:

npm i resend

Nyní můžete použít RESEND_API_KEY proměnná ve vašem kódu.

7. Odešlete e-mail pomocí Resend

Ve vašem src/index.ts souboru importujte balíček Resend a aktualizujte queue() handler pro odeslání e-mailu.

src/index.ts
import { Resend } from "resend";

interface Message {
	email: string;
}

export default {
	async fetch(req, env, ctx): Promise<Response> {
		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, env, ctx): Promise<void> {
		// 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: "[email protected]",
					to: [message.body.email],
					subject: "Hello World",
					html: "<strong>Sending an email from Worker!</strong>",
				});

				// 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 });
			}
		}
	},
} satisfies ExportedHandler<Env, Message>;

queue() handler nyní odešle e-mail pomocí Resend API. Zároveň kontroluje, zda se odeslání e-mailu nezdařilo, a v takovém případě zprávu zopakuje.

Konečný skript najdete níže:

src/index.ts
import { Resend } from "resend";

interface Message {
	email: string;
}

export default {
	async fetch(req, env, ctx): Promise<Response> {
		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, env, ctx): Promise<void> {
		// 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: "[email protected]",
					to: [message.body.email],
					subject: "Hello World",
					html: "<strong>Sending an email from Worker!</strong>",
				});

				// 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 });
			}
		}
	},
} satisfies ExportedHandler<Env, Message>;

Chcete-li aplikaci otestovat, spusťte vývojový server pomocí následujícího příkazu:

Spuštění vývojového serveru
npm run dev

Pomocí následujícího příkazu cURL odešlete požadavek do aplikace:

Otestujte pomocí požadavku cURL
curl -X POST -d "[email protected]" http://localhost:8787/

Na dashboardu Resend byste měli vidět, že e-mail byl odeslán na zadanou e-mailovou adresu.

8. Nasaďte svůj Worker

Chcete-li nasadit svůj Worker, spusťte následující příkaz:

Nasaďte svůj Worker
npx wrangler deploy

Nakonec přidejte klíč Resend API pomocí následujícího příkazu:

Přidejte API klíč Resend
npx wrangler secret put RESEND_API_KEY

Zadejte hodnotu svého API klíče. API klíč se přidá do vašeho projektu. Nyní můžete použít RESEND_API_KEY proměnná ve vašem kódu.

Úspěšně jste vytvořili Worker, který dokáže odesílat e-maily pomocí Resend API při dodržení limitů četnosti požadavků.

Pro otestování Workeru můžete použít následující požadavek cURL. Nahraďte <YOUR_WORKER_URL> URL adresou vašeho nasazeného Workeru.

Otestujte pomocí požadavku cURL
curl -X POST -d "[email protected]" <YOUR_WORKER_URL>

Viz Repozitář GitHub ↗ pro úplný kód tohoto tutoriálu. Pokud používáte Hono ↗, můžete se podívat na Příklad s Hono ↗.