Skip to content

More Examples

Beyond the core examples covered in previous pages, Thunder ships with many more edge functions that demonstrate specific Web APIs and patterns. All examples are in the examples/ directory of the repository and can be run with thunder watch --path ./examples/<name>/<name>.ts.

Directory: examples/websocket/

Upgrades an HTTP connection to WebSocket using Deno.upgradeWebSocket(). The server echoes back every message and responds to greetings.

export default function handler(req: Request) {
const { socket, response } = Deno.upgradeWebSocket(req);
socket.onmessage = (event) => {
const message = event.data;
socket.send(
JSON.stringify({
type: "echo",
message,
receivedAt: new Date().toISOString(),
})
);
};
return response;
}

Limits: Thunder enforces a maximum of 128 concurrent WebSocket connections per isolate and a 30-second idle timeout per socket.

Directory: examples/server-sent-events/

Streams events from server to client using a ReadableStream with text/event-stream content type. Each event is formatted as event: <type>\ndata: <json>\n\n.

import { StreamResponse } from "thunder:http";
export default function handler(req: Request) {
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 10; i++) {
await new Promise((resolve) => setTimeout(resolve, 1000));
const msg = `event: update\ndata: ${JSON.stringify({ counter: i + 1 })}\n\n`;
controller.enqueue(msg);
}
controller.close();
},
});
return StreamResponse(stream)
.header("content-type", "text/event-stream")
.header("cache-control", "no-cache")
.toResponse();
}

SSE is unidirectional (server to client) and works over standard HTTP, making it simpler than WebSocket for push-only use cases.

Directory: examples/wasm/

Loads a WebAssembly module (a Fibonacci calculator compiled from WAT) and calls its exported function from JavaScript.

import { JSONResponse } from "thunder:http";
const wasmCode = new Uint8Array([/* ... wasm bytes ... */]);
export default async function handler(req: Request) {
const url = new URL(req.url);
const n = parseInt(url.searchParams.get("n") || "5");
const wasmModule = await WebAssembly.instantiate(wasmCode);
const fibonacci = wasmModule.instance.exports.fibonacci as (n: number) => number;
const result = fibonacci(n);
return JSONResponse({ function: "fibonacci", input: n, result }).toResponse();
}

In production, you would compile the WASM module from Rust, C, or AssemblyScript rather than embedding raw bytes.

Directory: examples/preact-ssr/

Server-side rendering with Preact-style components. The example defines simple component functions (Card, Layout, Button) that return HTML strings, composed into a full page and served with text/html.

This pattern works without external dependencies — no npm install required. For production SSR, you would import preact-render-to-string from an eszip bundle.

Directory: examples/basic-auth/

Parses the Authorization: Basic <base64> header, decodes credentials, and performs a timing-safe comparison using crypto.subtle.sign("HMAC", ...) to prevent timing attacks.

// Decode credentials
const credentials = atob(authHeader.slice(6));
const [username, password] = credentials.split(":");
// Timing-safe comparison via HMAC
const key = await crypto.subtle.importKey(
"raw", new Uint8Array(32),
{ name: "HMAC", hash: "SHA-256" }, false, ["sign"]
);
const sig1 = await crypto.subtle.sign("HMAC", key, encoder.encode(username));
const sig2 = await crypto.subtle.sign("HMAC", key, encoder.encode(VALID_USERNAME));
// Compare byte-by-byte with constant-time XOR

Public routes (/health, /login) bypass authentication.

Directory: examples/cors/

Demonstrates configurable Cross-Origin Resource Sharing with:

  • An allowlist of origins (http://localhost:3000, https://example.com, etc.).
  • Preflight OPTIONS handling that returns 204 No Content with the required headers.
  • access-control-allow-credentials: true for cookie-based flows.
  • access-control-max-age: 3600 to cache preflight responses.

The CORS headers are added via a helper function that inspects the request’s Origin header against the allowlist.

Directory: examples/compression-stream/

Uses the Web Streams CompressionStream and DecompressionStream APIs to compress and decompress data with gzip or deflate.

// Compress
const compressed = ReadableStream.from([input])
.pipeThrough(new CompressionStream("gzip"));
// Decompress
const decompressed = ReadableStream.from([compressed])
.pipeThrough(new DecompressionStream("gzip"));

The example exposes POST /api/compress and POST /api/decompress endpoints that report original size, compressed size, and compression ratio.

The following examples are also available in the repository:

ExampleWhat it demonstrates
abort-controllerAbortController for request cancellation and fetch timeouts.
cachingCache-Control, ETag, and conditional 304 Not Modified responses.
data-processingArray/object transformation pipelines.
error-handlingStructured error responses with stack traces in development mode.
form-handlingFormData parsing for multipart/form-data and application/x-www-form-urlencoded.
generatorsGenerator functions and async iterators for streaming data.
html-pageServing a complete HTML page with inline CSS and JS.
http-requestOutbound fetch() calls to external APIs.
intl-apiIntl.DateTimeFormat, Intl.NumberFormat, Intl.Collator.
middlewareRequest/response middleware chain (logging, auth, timing).
performance-apiperformance.now() and performance.mark() for timing.
rate-limitingIn-memory sliding-window rate limiter.
request-modificationCloning requests and modifying headers before forwarding.
security-headersCSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy.
transform-streamTransformStream for on-the-fly data transformation.
url-redirect301/302 redirects with Response.redirect().
urlpattern-routingURLPattern API for declarative route matching.
web-crypto-apiHashing (SHA-256), HMAC signing, AES encryption/decryption.

Browse the full source in examples/ or see the Examples overview for the complete catalog.