Runtime APIs
OpenWorkers implements standard Web APIs compatible with Cloudflare Workers and the WinterCG spec.
Contents
- Fetch API — fetch, Request, Response, Headers
- URL APIs — URL, URLSearchParams
- Text Encoding — TextEncoder, TextDecoder, Base64
- Binary Data — Blob, File, FormData
- Streams — ReadableStream
- Crypto — Random, Hashing, HMAC
- Timers — setTimeout, setInterval
- Abort Controller — Cancel operations
- Console — Logging
- Other APIs — structuredClone, performance
- Limitations — What’s not supported
Fetch API
fetch()
Make HTTP requests from your worker.
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
});
const data = await response.json(); Supported methods: GET, POST, PUT, DELETE, PATCH, HEAD.
Request
const request = new Request('https://example.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ hello: 'world' })
});
// Properties
request.method; // 'POST'
request.url; // 'https://example.com'
request.headers; // Headers object
// Body methods
await request.text(); // Read as string
await request.json(); // Parse as JSON
await request.arrayBuffer(); // Read as ArrayBuffer
await request.formData(); // Parse as FormData
// Clone (useful when body needs to be read multiple times)
const clone = request.clone(); Response
// Simple response
new Response('Hello World');
// With options
new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
// Properties
response.status; // 200
response.statusText; // 'OK'
response.ok; // true (status 200-299)
response.headers; // Headers object
// Body methods
await response.text();
await response.json();
await response.arrayBuffer();
await response.formData(); Headers
const headers = new Headers({
'Content-Type': 'application/json',
'X-Custom': 'value'
});
headers.set('Authorization', 'Bearer token');
headers.append('Accept', 'application/json');
headers.get('Content-Type'); // 'application/json'
headers.has('X-Custom'); // true
headers.delete('X-Custom');
// Iterate
for (const [name, value] of headers) {
console.log(name, value);
} URL APIs
URL
Parse and manipulate URLs.
const url = new URL('https://example.com:8080/path?foo=bar#hash');
url.protocol; // 'https:'
url.host; // 'example.com:8080'
url.hostname; // 'example.com'
url.port; // '8080'
url.pathname; // '/path'
url.search; // '?foo=bar'
url.hash; // '#hash'
url.origin; // 'https://example.com:8080'
// Modify
url.pathname = '/new-path';
url.searchParams.set('key', 'value');
url.href; // 'https://example.com:8080/new-path?foo=bar&key=value#hash' Relative URLs with base:
const url = new URL('/api/users', 'https://example.com');
url.href; // 'https://example.com/api/users' URLSearchParams
const params = new URLSearchParams('foo=1&bar=2');
params.get('foo'); // '1'
params.getAll('foo'); // ['1']
params.has('bar'); // true
params.set('foo', '3');
params.append('baz', '4');
params.delete('bar');
params.toString(); // 'foo=3&baz=4'
// From object
const params2 = new URLSearchParams({ a: '1', b: '2' });
// Iterate
for (const [key, value] of params) {
console.log(key, value);
} Text Encoding
TextEncoder
Encode strings to UTF-8 bytes.
const encoder = new TextEncoder();
const bytes = encoder.encode('Hello World');
// Uint8Array(11) [72, 101, 108, 108, 111, ...] TextDecoder
Decode UTF-8 bytes to strings.
const decoder = new TextDecoder();
const text = decoder.decode(new Uint8Array([72, 101, 108, 108, 111]));
// 'Hello' Base64
// Encode string to base64
const encoded = btoa('Hello World'); // 'SGVsbG8gV29ybGQ='
// Decode base64 to string
const decoded = atob('SGVsbG8gV29ybGQ='); // 'Hello World' Binary Data
Blob
Immutable raw binary data.
const blob = new Blob(['Hello ', 'World'], { type: 'text/plain' });
blob.size; // 11
blob.type; // 'text/plain'
await blob.text(); // 'Hello World'
await blob.arrayBuffer(); // ArrayBuffer
blob.slice(0, 5); // New Blob with 'Hello' File
A Blob with a name and last modified date.
const file = new File(['content'], 'file.txt', {
type: 'text/plain',
lastModified: Date.now()
});
file.name; // 'file.txt'
file.lastModified; // timestamp FormData
Handle multipart form data.
const form = new FormData();
form.append('name', 'John');
form.append('file', new Blob(['data']), 'file.txt');
form.get('name'); // 'John'
form.getAll('name'); // ['John']
form.has('file'); // true
form.delete('name');
// Iterate
for (const [key, value] of form) {
console.log(key, value);
} Parse incoming form data:
addEventListener('fetch', async (event) => {
const form = await event.request.formData();
const name = form.get('name');
const file = form.get('file'); // File object
}); Streams
ReadableStream
Create streaming responses (SSE, chunked data).
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode('Hello '));
controller.enqueue(new TextEncoder().encode('World'));
controller.close();
}
});
new Response(stream, {
headers: { 'Content-Type': 'text/plain' }
}); Server-Sent Events example:
const stream = new ReadableStream({
async start(controller) {
const encoder = new TextEncoder();
for (let i = 0; i < 5; i++) {
controller.enqueue(encoder.encode(`data: Message ${i}
`));
await new Promise((r) => setTimeout(r, 1000));
}
controller.close();
}
});
new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache'
}
}); Reading a stream:
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(new TextDecoder().decode(value));
} Split a stream with tee():
const [stream1, stream2] = stream.tee(); Crypto
Random values
// Generate random UUID
crypto.randomUUID(); // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
// Fill array with random bytes
const array = new Uint8Array(16);
crypto.getRandomValues(array); Hashing (digest)
const data = new TextEncoder().encode('Hello World');
const hash = await crypto.subtle.digest('SHA-256', data);
// ArrayBuffer
// Convert to hex
const hashArray = Array.from(new Uint8Array(hash));
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); Supported algorithms: SHA-1, SHA-256, SHA-384, SHA-512.
HMAC signing
const encoder = new TextEncoder();
const keyData = encoder.encode('secret-key');
const data = encoder.encode('message to sign');
// Import key
const key = await crypto.subtle.importKey('raw', keyData, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign', 'verify']);
// Sign
const signature = await crypto.subtle.sign('HMAC', key, data);
// Verify
const isValid = await crypto.subtle.verify('HMAC', key, signature, data); Timers
// Execute after delay
const timeoutId = setTimeout(() => {
console.log('Executed after 1 second');
}, 1000);
clearTimeout(timeoutId); // Cancel
// Execute repeatedly
const intervalId = setInterval(() => {
console.log('Every 500ms');
}, 500);
clearInterval(intervalId); // Cancel
// Queue microtask
queueMicrotask(() => {
console.log('Runs before next event loop tick');
}); Abort Controller
Cancel fetch requests or other async operations.
const controller = new AbortController();
// Abort after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://slow-api.example.com', {
signal: controller.signal
});
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request was aborted');
}
} Console
console.log('Info message');
console.info('Info message');
console.warn('Warning message');
console.error('Error message');
console.debug('Debug message');
// Objects are JSON-serialized
console.log({ user: 'john', id: 123 }); Other APIs
structuredClone
Deep clone any value.
const original = { nested: { value: 42 }, array: [1, 2, 3] };
const clone = structuredClone(original);
clone.nested.value = 100;
original.nested.value; // Still 42 performance.now()
High-resolution timestamp (milliseconds since worker start).
const start = performance.now();
// ... do work ...
const duration = performance.now() - start;
console.log(`Took ${duration}ms`); Global aliases
globalThis; // The global object
self; // Alias (Web Worker compatibility)
global; // Alias (Node.js compatibility) Limitations
- No dynamic imports — Code must be pre-bundled. Use
esbuildor similar to bundle your code before deploying. Theexport defaulthandler syntax works, butimport './module.js'at runtime does not. - No DOM — No
document,window, or browser-specific APIs - No WebSocket — Only HTTP via Fetch API
- No Node.js APIs — No
fs,path,process, etc. - Single-threaded — No Web Workers or shared memory