Edge runtimes
Run @hatched/sdk-js on Cloudflare Workers, Vercel Edge, Deno, and Bun — fetch overrides, AbortSignal, and the crypto caveat.
The SDK is written against native web standards — fetch, Response,
AbortSignal, crypto.randomUUID — so it runs unmodified on every modern
edge runtime.
Cloudflare Workers
import { HatchedClient } from '@hatched/sdk-js';
export interface Env {
HATCHED_API_KEY: string;
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const hatched = new HatchedClient({ apiKey: env.HATCHED_API_KEY });
const health = await hatched.health();
return Response.json(health);
},
};The server-only guard passes because Workers don't have a window or
document global.
Webhooks on Workers
WebhooksResource.verifySignature uses node:crypto, which does not
run on Workers. For Workers, verify manually with Web Crypto:
async function verify(body: string, header: string, secret: string): Promise<boolean> {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const ts = parts.t;
const sig = parts.v1;
if (!ts || !sig) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const expected = await crypto.subtle.sign(
'HMAC',
key,
new TextEncoder().encode(`${ts}.${body}`),
);
const expectedHex = Array.from(new Uint8Array(expected))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
return timingSafeEqualHex(expectedHex, sig);
}
function timingSafeEqualHex(a: string, b: string): boolean {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
return diff === 0;
}Vercel Edge
// app/api/hatched/health/route.ts
export const runtime = 'edge';
import { HatchedClient } from '@hatched/sdk-js';
export async function GET() {
const hatched = new HatchedClient({ apiKey: process.env.HATCHED_API_KEY! });
return Response.json(await hatched.health());
}For webhook verification with node:crypto, switch to
runtime = 'nodejs'. Everything else (reads, event sends, widget session
mint) works on Edge.
Deno
import { HatchedClient } from 'npm:@hatched/sdk-js';
const hatched = new HatchedClient({ apiKey: Deno.env.get('HATCHED_API_KEY')! });
console.log(await hatched.health());Bun
import { HatchedClient } from '@hatched/sdk-js';
const hatched = new HatchedClient({ apiKey: Bun.env.HATCHED_API_KEY! });Custom fetch override
Pass your own fetch — useful for:
- Preflighting every request through a telemetry hop
- Forcing a specific outbound pool on Workers (
fetch(input, { cf: {...} })) - Injecting retries via a shared HTTP client
const hatched = new HatchedClient({
apiKey: env.HATCHED_API_KEY,
fetch: async (input, init) => {
const start = Date.now();
const res = await fetch(input, init);
metrics.record('hatched.http', Date.now() - start);
return res;
},
});Cancellation
AbortSignal.any is used internally to combine your signal with the SDK
timeout. If your runtime doesn't have AbortSignal.any (very old
environments), the SDK falls back to a manual combinator — no action
needed on your side.