new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Cloudflare Workers · all subjects

code-patterns

261 notes in this subject, read out of this brain and free to use. This is page 3 of 5.

TypeScript example: read POST request body with type hints

```ts async function readRequestBody(request: Request) { const contentType = request.headers.get('content-type'); if (contentType.includes('application/json')) { return JSON.stringify(await request.json()); } else if (contentType.includes('application/text')) { return request.text(); } else if (contentType.includes('text/html')) { return request.text(); } else if (contentType.includes('form')) { const formData = await request.formData(); const body = {}; for (const entry of formData.entries()) { body[entry[0]] = entry[1]; } return JSON.stringify(body); } else { return 'a file'; } } ``` This example shows typed request body reading for different content types.

Rust example: read POST request body with content-type matching

```rs async fn read_request_body(mut req: Request) -> String { let ctype = req.headers().get('content-type').unwrap().unwrap(); match ctype.as_str() { 'application/json' => format!('{:?}', req.json::<Payload>().await.unwrap()), 'text/html' => req.text().await.unwrap(), 'multipart/form-data' => format!('{:?}', req.form_data().await.unwrap()), _ => String::from('a file'), } } ``` This example shows how to match on content-type and read POST bodies in Rust.

Hono framework example: read POST request body and serve forms

```ts async function readRequestBody(request: Request): Promise<string> { const contentType = request.headers.get('content-type') || ''; if (contentType.includes('application/json')) { const body = await request.json(); return JSON.stringify(body); } else if (contentType.includes('form')) { const formData = await request.formData(); const body: Record<string, string> = {}; for (const [key, value] of formData.entries()) { body[key] = value.toString(); } return JSON.stringify(body); } else { return await request.text(); } } app.get('*', async (c) => { const url = c.req.url; if (url.includes('form')) { return c.html(someForm); } return c.text('The request was a GET'); }); app.post('*', async (c) => { const reqBody = await readRequestBody(c.req.raw); const retBody = `The request body sent in was ${reqBody}`; return c.text(retBody); }); ``` This example shows how to handle POST requests and serve HTML forms using the Hono framework.

Return JSON with Response.json() in TypeScript

In TypeScript, use Response.json(data) to return JSON directly from a Worker fetch handler. The method accepts a data object and automatically sets the correct content-type header.

ExportedHandler type for TypeScript Workers

TypeScript Worker exports should be typed with 'satisfies ExportedHandler' to ensure the default export matches the required Worker interface with the async fetch method.

Return JSON with Hono framework

In Hono, use c.json(data) in route handlers to return JSON responses. The context object c provides the json() method for automatic serialization and content-type handling.

Return JSON with Response::from_json in Rust

In Rust, use Response::from_json(&data) to return JSON from a Worker. The data must derive Serialize and Deserialize traits from serde.

Return JSON with response headers in Python

In Python, use json.dumps() to serialize data and return a Response object with the serialized JSON as the body and a headers dictionary containing 'content-type': 'application/json'.

HTMLRewriter basic usage for link rewriting

The HTMLRewriter API allows you to parse and transform HTML. To rewrite links in HTML, create a handler class that implements an element() method to get and set attributes, then instantiate HTMLRewriter and chain .on() calls to specify which elements and handlers to apply. Call .transform(response) to apply the transformations to a Response object.

HTMLRewriter available in Python Workers

HTMLRewriter is available in Python Workers. Import it from the js module, create handler classes with an element() method, and use create_proxy() from pyodide.ffi to wrap Python handler instances before passing them to HTMLRewriter.on() methods.

HTMLRewriter with Hono framework

When using the Hono framework with Workers, instantiate HTMLRewriter inside route handlers and call rewriter.transform(response) on responses. Access the raw request via c.req.raw and ensure you preserve response headers when returning the transformed response.

Check Content-Type before using HTMLRewriter

Always check that the response Content-Type header starts with 'text/html' before calling HTMLRewriter.transform(). If the response is not HTML, pass through the response unchanged, as HTMLRewriter is only designed for HTML content.

HTMLRewriter example: rewrite href and src attributes

This example shows how to replace occurrences of 'developer.mozilla.org' with 'mynewdomain.com' in href attributes of <a> tags and src attributes of <img> tags: ```js class AttributeRewriter { constructor(attributeName) { this.attributeName = attributeName; } element(element) { const attribute = element.getAttribute(this.attributeName); if (attribute) { element.setAttribute( this.attributeName, attribute.replace(OLD_URL, NEW_URL), ); } } } const rewriter = new HTMLRewriter() .on("a", new AttributeRewriter("href")) .on("img", new AttributeRewriter("src")); const res = await fetch(request); const contentType = res.headers.get("Content-Type"); if (contentType.startsWith("text/html")) { return rewriter.transform(res); } else { return res; } ```

Stream and transform JSON response example

Fetch a large JSON response, transform fields, and stream it back to the client: import { JSONParser } from "@streamparser/json-whatwg"; export default { async fetch(request) { const response = await fetch("https://api.example.com/large-dataset.json"); const parser = new JSONParser({ paths: ["$.items.*"] }); const { readable, writable } = new TransformStream(); const writer = writable.getWriter(); const encoder = new TextEncoder(); // Process the upstream response in the background (async () => { const reader = response.body .pipeThrough(parser) .getReader(); await writer.write(encoder.encode('{"processedItems":[')); let first = true; while (true) { const { done, value } = await reader.read(); if (done) break; // Transform each item as it streams through const item = value.value; const transformed = { id: item.id, title: item.title.toUpperCase(), processed: true, }; if (!first) await writer.write(encoder.encode(",")); first = false; await writer.write(encoder.encode(JSON.stringify(transformed))); } await writer.write(encoder.encode("]}")); await writer.close(); })(); return new Response(readable, { headers: { "Content-Type": "application/json" }, }); }, };

Use @streamparser/json-whatwg library for streaming JSON

The @streamparser/json-whatwg library provides a streaming JSON parser compatible with the Web Streams API. Install it with: npm install @streamparser/json-whatwg

Stream and parse JSON request body example

Parse a large JSON request body extractively: import { JSONParser } from "@streamparser/json-whatwg"; export default { async fetch(request) { const parser = new JSONParser({ paths: ["$.users.*"] }); const users = []; // Pipe the request body through the JSON parser const reader = request.body .pipeThrough(parser) .getReader(); // Process matching JSON values as they stream in while (true) { const { done, value } = await reader.read(); if (done) break; // Extract only the name field from each user object if (value.value?.name) { users.push(value.value.name); } } return Response.json({ userNames: users }); }, };

JSONParser paths option syntax

The JSONParser accepts a paths option that uses JSONPointer syntax to specify which parts of the JSON to parse. For example, "$.users.*" matches all elements in the users array, and "$.items.*" matches all elements in the items array.

Consume prefetched bootstrap data in SPA client code

On the client, read `window.__BOOTSTRAP_DATA__` before making any API calls. If the data exists, use it directly. Otherwise, fall back to a normal fetch. Add a TypeScript type declaration for the global property: `declare global { interface Window { __BOOTSTRAP_DATA__?: unknown; } }`.

Client-side bootstrap data consumption example

```tsx import { useEffect, useState } from "react"; function App() { const [data, setData] = useState(window.__BOOTSTRAP_DATA__ || null); const [loading, setLoading] = useState(!data); useEffect(() => { if (data) return; // Already have prefetched data — skip the API call. fetch("/api/bootstrap") .then((res) => res.json()) .then((result) => { setData(result); setLoading(false); }); }, []); if (loading) return <LoadingSpinner />; return <Dashboard data={data} />; } ```

HTMLRewriter inject meta tags for social media crawlers

Use HTMLRewriter to inject Open Graph or other <meta> tags into the <head> based on the request path. This gives social-media crawlers correct previews without requiring a full server-side rendering framework.

HTMLRewriter inject meta tags example

```ts new HTMLRewriter() .on("head", { element(el) { el.append(`<meta property="og:title" content="${title}" />`, { html: true, }); }, }) .transform(shell); ```

HTMLRewriter inject user configuration example

```ts new HTMLRewriter() .on("body", { element(el) { el.prepend( `<script>window.__APP_CONFIG__=${JSON.stringify({ apiBase: env.API_BASE_URL, featureFlags: { darkMode: true }, })}</script>`, { html: true }, ); }, }) .transform(shell); ```

HTMLRewriter supports framework-agnostic SPA shell pattern

The HTMLRewriter bootstrap data injection pattern works with any SPA framework including React, Vue, Svelte, and others. The same client-side consumption pattern applies across all frameworks.

HTMLRewriter inject bootstrap data into SPA shell

Use HTMLRewriter to fetch bootstrap API data in parallel with the SPA shell HTML, then inject the serialized data into a <script> tag in the <body>. This allows the SPA to have all necessary data before its JavaScript runs, eliminating client-side data fetching on initial load. If the API call fails, the shell still loads and the SPA falls back to client-side data fetching.

SPA shell bootstrap data injection example with Static Assets

```ts export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); // Serve root-level static files directly. // Hashed assets under /assets/* skip the Worker entirely via run_worker_first. if (url.pathname.match(/\.\w+$/) && !url.pathname.endsWith(".html")) { return env.ASSETS.fetch(request); } // Start fetching bootstrap data immediately — do not await yet. const dataPromise = fetchBootstrapData(env, url.pathname, request.headers); // Fetch the SPA shell from static assets (co-located, sub-millisecond). const shell = await env.ASSETS.fetch( new Request(new URL("/index.html", request.url)), ); // Use HTMLRewriter to stream the shell and inject data into <body>. return new HTMLRewriter() .on("body", { async element(el) { const data = await dataPromise; if (data) { el.prepend( `<script>window.__BOOTSTRAP_DATA__=${JSON.stringify(data)}</script>`, { html: true }, ); } }, }) .transform(shell); }, } satisfies ExportedHandler<Env>; async function fetchBootstrapData( env: Env, pathname: string, headers: Headers, ): Promise<unknown | null> { try { const res = await fetch(`${env.API_BASE_URL}/api/bootstrap`, { headers: { Cookie: headers.get("Cookie") || "", "X-Request-Path": pathname, }, }); if (!res.ok) return null; return await res.json(); } catch { // If the API is down, the shell still loads and the SPA // falls back to client-side data fetching. return null; } } ```

SPA shell bootstrap data injection example with external origin

```ts export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); // Pass static asset requests through to the external origin unmodified. if (url.pathname.match(/\.\w+$/) && !url.pathname.endsWith(".html")) { return fetch(new Request(`${env.SPA_ORIGIN}${url.pathname}`, request)); } // Start fetching bootstrap data immediately — do not await yet. const dataPromise = fetchBootstrapData(env, url.pathname, request.headers); // Fetch the SPA shell from the external origin. // SPA routers serve index.html for all routes. const shell = await fetch(`${env.SPA_ORIGIN}/index.html`); if (!shell.ok) { return new Response("Origin returned an error", { status: 502 }); } // Use HTMLRewriter to stream the shell and inject data into <body>. return new HTMLRewriter() .on("body", { async element(el) { const data = await dataPromise; if (data) { el.prepend( `<script>window.__BOOTSTRAP_DATA__=${JSON.stringify(data)}</script>`, { html: true }, ); } }, }) .transform(shell); }, } satisfies ExportedHandler<Env>; async function fetchBootstrapData( env: Env, pathname: string, headers: Headers, ): Promise<unknown | null> { try { const res = await fetch(`${env.API_BASE_URL}/api/bootstrap`, { headers: { Cookie: headers.get("Cookie") || "", "X-Request-Path": pathname, }, }); if (!res.ok) return null; return await res.json(); } catch { // If the API is down, the shell still loads and the SPA // falls back to client-side data fetching. return null; } } ```

Hono middleware pattern for HTMLRewriter with response cloning

When using Hono, apply HTMLRewriter in middleware by cloning the response, reading its body as text, creating a new Response object with the modified body, and assigning it back to c.res. First check the content-type header to ensure it is text/html before processing.

HTMLRewriter element.append() with HTML content

The element.append() method can add HTML content to an element. Pass the HTML string as the first argument and {html: true} as the second argument to interpret the string as HTML rather than plain text. Example: element.append('<script src="..."></script>', {html: true}).

HTMLRewriter element.getAttribute() to match elements

Use element.getAttribute() to retrieve an attribute value and perform conditional transformations. For example, check if element.getAttribute('id') equals a specific value before appending content to that element.

Turnstile script URL

The Turnstile API script is hosted at https://challenges.cloudflare.com/turnstile/v0/api.js and should be loaded with async and defer attributes.

Turnstile widget HTML structure

A Turnstile widget is rendered as a div element with the class 'cf-turnstile' and a data-sitekey attribute containing the site key. Optional attributes include data-theme which can be set to 'light'. Example: <div class="cf-turnstile" data-sitekey="${SITE_KEY}" data-theme="light"></div>.

Turnstile token verification endpoint

Turnstile tokens are verified by sending a POST request to https://challenges.cloudflare.com/turnstile/v0/siteverify with FormData containing the fields: secret (the Turnstile Secret key), response (the token from cf-turnstile-response), and optionally remoteip (the client IP address).

Turnstile token field name in form data

When a Turnstile widget is rendered, it injects a token into the form data under the field name 'cf-turnstile-response'. This token must be retrieved and verified server-side.

Turnstile verification response format

The Turnstile siteverify API returns a JSON response with a 'success' boolean field indicating whether the token was valid. If success is false, the token validation failed and the request should be rejected.

Turnstile implementation requires two parts

Implementing Turnstile in Workers requires two parts: (1) injecting the Turnstile widget into HTML using HTMLRewriter, and (2) verifying the resulting token server-side using the Siteverify API before processing the form submission.

Python Workers HTMLRewriter usage

In Python Workers, use pyodide.ffi.create_proxy() to wrap Python classes as handlers, then pass them to HTMLRewriter.new().on(). Each handler class must implement an element() method that receives the element to transform.

HTMLRewriter API basic usage pattern

HTMLRewriter is a runtime API that allows matching and transforming specific HTML elements in a response. Use the .on() method to attach an element handler for a specific tag, then call .transform(response) to apply the transformations. For example: new HTMLRewriter().on('head', { element(element) { /* handler */ } }).transform(res).

Route preloading with Speculation Rules API

When preload: true is set on a static mount route, the router automatically preloads those routes to enable faster navigation. For Chromium-based browsers (Chrome, Edge, Opera, Brave), the router uses the Speculation Rules API, a modern browser-native prefetching mechanism that injects <script type="speculationrules"> into the <head> element. The browser handles prefetching automatically with optimal priority management, respects user preferences (battery saver, data saver modes), uses per-document in-memory cache for faster access, is not blocked by Cache-Control headers, and is more efficient than JavaScript-based fetching.

JavaScript module export syntax required

A Worker must use 'export default' JavaScript syntax to define a JavaScript module. The exported object contains properties corresponding to the events your Worker should handle, such as the fetch handler.

Modular Python Workers

Python Workers can be split across multiple files using standard Python import statements. When entry point files are edited, pywrangler automatically detects changes and reloads the Worker.

Cron trigger in Python Worker

Implement an async scheduled(self, controller, env, ctx) method in a WorkerEntrypoint subclass to handle cron triggers. All four parameters (self, controller, env, ctx) are required for scheduled methods, unlike fetch() which only requires self and request.

Read bundled asset files in Python Worker

Use pathlib.Path to read bundled asset files. Get the file path relative to the current module using Path(__file__).parent / "filename". For example: html_file = Path(__file__).parent / "file.html"; return Response(html_file.read_text(), headers={"Content-Type": "text/html"}).

Parse URL and query parameters in Python Worker

Use urllib.parse.urlparse to parse the request URL and urllib.parse.parse_qs to extract query parameters into a Python dictionary. Access the URL path via url.path and query parameters via params. For example: url = urlparse(request.url); params = parse_qs(url.query); name = params["name"][0].

Import local modules in Python Worker

To import a local module in a Python Worker, use the module name directly without specifying the directory. For example, if your Worker has src/main.py and src/module.py, use `import module` in main.py. The main module path is specified in wrangler.toml with `main = "src/main.py"`, and the src directory does not need to be included in the import statement.

Workflow steps in Python Worker

Extend WorkflowEntrypoint and implement async run(self, event, step) method. Define workflow steps using @step.do() decorator on async functions. Pass concurrent=True to @step.do(concurrent=True) to run steps concurrently. Pass step function results as parameters to dependent steps. For example: @step.do(); async def step_a(): return 10; @step.do(concurrent=True); async def final_step(step_a): return step_a.

Python Worker basic structure with WorkerEntrypoint

A basic Python Worker includes a Python file with a Default class extending WorkerEntrypoint that implements an async fetch method. Example: from workers import Response, WorkerEntrypoint; class Default(WorkerEntrypoint): async def fetch(self, request): return Response("Hello world!")

addEventListener handleEvent function support

As of 2021-09-24, addEventListener supports listeners passed as either a function or an object with a handleEvent member function.

WebSocket.close arguments optional

As of 2021-04-29, the arguments to WebSocket.close() are now optional, as the standard requires.

WebCrypto wrapKey/unwrapKey for AES

As of 2021-04-29, WebCrypto implements wrapKey() and unwrapKey() for AES algorithms.

WebCrypto HKDF implementation

As of 2021-05-14, WebCrypto implements HKDF.

WebCrypto RSA-OAEP support

As of 2021-05-14, WebCrypto adds support for RSA-OAEP.

WebCrypto JWK export for RSA, ECDSA, ECDH

As of 2021-05-14, WebCrypto supports JWK export for RSA, ECDSA, and ECDH key types.

WebCrypto ECDSA/ECDH raw format

As of 2021-06-04, WebCrypto supports raw import/export format for ECDSA/ECDH public keys.

WebCrypto Ed25519 support

As of 2021-06-27, WebCrypto implements non-standard Ed25519 operation (algorithm NODE-ED25519, curve name NODE-ED25519). Raw import/export of private keys is disallowed, per parity with ECDSA/ECDH.

DOMException constructor string coercion

As of 2022-11-30, the DOMException constructor has been updated so that the message and name arguments can be any JavaScript value coercible into a string, not just strings.

TransformStream standard constructor support

As of 2022-06-03, it is possible to create standard TransformStream instances that perform transformations on data. The transformstream_enable_standard_constructor compatibility flag is required to enable this, as it changes the behavior of the default new TransformStream() with no arguments.

ReadableStream pipeTo and pipeThrough AbortSignal support

As of 2022-05-19, ReadableStream pipeTo and pipeThrough now support cancellation using AbortSignal.

AES-GCM zero-length IV error message

As of 2022-04-08, the AES-GCM implementation of the Web Cryptography API returns a friendlier error explaining that 0-length IVs are not allowed.

IdentityTransformStream for byte-oriented transforms

As of 2022-03-24, IdentityTransformStream creates a byte-oriented TransformStream that passes bytes through unmodified. The readable half supports BYOB-reads. It is identical to the current non-spec-compliant TransformStream implementation. New code should use IdentityTransformStream instead of new TransformStream() to avoid breaking changes.

ByteLengthQueuingStrategy and CountQueuingStrategy availability

As of 2022-03-17, the standard ByteLengthQueuingStrategy and CountQueuingStrategy classes are available.

Give your agent this brain