The API layer provides JavaScript runtime APIs to worker code. These implementations bridge Web Standards, Node.js compatibility, and Cloudflare-specific features.
Overview
The API layer is located in src/workerd/api/ and contains:
Core Web APIs (HTTP, streams, crypto, encoding)
Node.js compatibility layer
Cloudflare-specific APIs
TypeScript type definitions
Directory structure
src/workerd/api/
├── crypto/ # Web Crypto API
├── streams/ # Streams API (842-line README)
├── node/ # Node.js compatibility (C++)
├── tests/ # Runtime API tests
└── *.{h,c++} # Core API implementations
src/node/ # Node.js compatibility (TypeScript)
src/cloudflare/ # Cloudflare-specific APIs (TypeScript)
Core API patterns
Basic API structure
Most APIs follow this pattern:
// my-api.h
#pragma once
#include <workerd/jsg/jsg.h>
namespace workerd :: api {
class MyAPI : public jsg :: Object {
public:
// Constructor
static jsg :: Ref < MyAPI > constructor ( jsg :: Lock & js );
// Methods
kj :: String doSomething ();
jsg :: Promise < int > doAsync ( jsg :: Lock & js );
// Properties
kj :: String getName () { return name; }
void setName ( kj :: String newName ) { name = kj :: mv (newName); }
JSG_RESOURCE_TYPE ( MyAPI ) {
JSG_METHOD (constructor);
JSG_METHOD (doSomething);
JSG_METHOD (doAsync);
JSG_PROTOTYPE_PROPERTY (name, getName, setName);
}
void visitForGc ( jsg :: GcVisitor & visitor ) {
// Visit any GC-tracked fields
}
private:
kj ::String name;
};
} // namespace workerd::api
Implementation file
// my-api.c++
#include "my-api.h"
namespace workerd :: api {
jsg :: Ref < MyAPI > MyAPI :: constructor ( jsg :: Lock & js ) {
return jsg :: alloc < MyAPI >();
}
kj :: String MyAPI :: doSomething () {
return kj :: str ( "result" );
}
jsg :: Promise < int > MyAPI :: doAsync ( jsg :: Lock & js ) {
auto & context = IoContext :: current ();
auto promise = context . someAsyncOperation ();
return context . awaitIo (js, kj :: mv (promise),
[]( jsg :: Lock & js , Result result ) {
return processResult (result);
});
}
} // namespace workerd::api
HTTP APIs
Request and Response
class Request : public jsg :: Object {
static jsg :: Ref < Request > constructor (
jsg :: Lock & js ,
kj :: String url ,
jsg :: Optional < RequestInit > init );
// Properties
kj :: String getUrl () { return url; }
kj :: String getMethod () { return method; }
jsg :: Ref < Headers > getHeaders () { return headers; }
// Body methods
jsg :: Promise < kj :: String > getText ( jsg :: Lock & js );
jsg :: Promise < jsg :: JsValue > getJson ( jsg :: Lock & js );
jsg :: Promise < kj :: Array < byte >> getArrayBuffer ( jsg :: Lock & js );
JSG_RESOURCE_TYPE ( Request ) {
JSG_METHOD (constructor);
JSG_READONLY_PROTOTYPE_PROPERTY (url, getUrl);
JSG_READONLY_PROTOTYPE_PROPERTY (method, getMethod);
JSG_READONLY_PROTOTYPE_PROPERTY (headers, getHeaders);
JSG_METHOD_NAMED (text, getText);
JSG_METHOD_NAMED (json, getJson);
JSG_METHOD_NAMED (arrayBuffer, getArrayBuffer);
}
};
fetch() implementation
jsg :: Promise < jsg :: Ref < Response >> fetch (
jsg :: Lock & js ,
kj :: OneOf < jsg :: Ref < Request >, kj :: String > requestOrUrl ,
jsg :: Optional < RequestInit > init ) {
// Parse request
auto request = parseRequest (requestOrUrl, init);
// Get HTTP client from IoContext
auto & context = IoContext :: current ();
auto & client = context . getHttpClient ();
// Make request
auto promise = client . request (
request . method ,
request . url ,
request . headers ,
kj :: mv ( request . body ));
// Bridge to JavaScript
return context . awaitIo (js, kj :: mv (promise),
[]( jsg :: Lock & js , HttpResponse response ) {
return jsg :: alloc < Response >( kj :: mv (response));
});
}
Streams API
The streams implementation is complex. See src/workerd/api/streams/README.md for details.
Key classes:
class ReadableStream : public jsg :: Object {
// Standard stream interface
jsg :: Ref < ReadableStreamDefaultReader > getReader ();
jsg :: Promise < void > cancel ( jsg :: Lock & js , jsg :: Optional < jsg :: JsValue > reason );
// Internal implementation
class Impl ;
};
class WritableStream : public jsg :: Object {
jsg :: Ref < WritableStreamDefaultWriter > getWriter ();
jsg :: Promise < void > abort ( jsg :: Lock & js , jsg :: Optional < jsg :: JsValue > reason );
};
class TransformStream : public jsg :: Object {
jsg :: Ref < ReadableStream > getReadable ();
jsg :: Ref < WritableStream > getWritable ();
};
Crypto API
Implemented in src/workerd/api/crypto/:
class SubtleCrypto : public jsg :: Object {
jsg :: Promise < kj :: Array < byte >> digest (
jsg :: Lock & js ,
kj :: String algorithm ,
jsg :: BufferSource data );
jsg :: Promise < jsg :: Ref < CryptoKey >> generateKey (
jsg :: Lock & js ,
KeyAlgorithm algorithm ,
bool extractable ,
kj :: Array < kj :: String > keyUsages );
jsg :: Promise < kj :: Array < byte >> encrypt (
jsg :: Lock & js ,
EncryptAlgorithm algorithm ,
jsg :: Ref < CryptoKey > key ,
jsg :: BufferSource data );
JSG_RESOURCE_TYPE ( SubtleCrypto ) {
JSG_METHOD (digest);
JSG_METHOD (generateKey);
JSG_METHOD (encrypt);
// ...
}
};
Node.js compatibility
The Node.js compatibility layer has two parts:
C++ layer (src/workerd/api/node/)
Native modules implemented in C++:
namespace workerd :: api :: node {
class CryptoImpl : public jsg :: Object {
// Node.js crypto implementations
kj :: Array < byte > randomBytes ( int size );
void randomFill ( jsg :: BufferSource buffer );
JSG_RESOURCE_TYPE ( CryptoImpl ) {
JSG_METHOD (randomBytes);
JSG_METHOD (randomFill);
}
};
} // namespace workerd::api::node
Register in src/workerd/api/node/node.h:
#define NODEJS_MODULES ( V ) \
V (buffer) \
V (crypto) \
V (util) \
// ...
TypeScript layer (src/node/)
JavaScript/TypeScript implementations:
// src/node/buffer.ts
import { BufferImpl } from 'node-internal:buffer' ;
export class Buffer extends Uint8Array {
static from ( value : any ) : Buffer {
return BufferImpl . from ( value );
}
static alloc ( size : number ) : Buffer {
return BufferImpl . alloc ( size );
}
}
Module types:
BUILTIN - Runtime modules (node:*, cloudflare:*)
INTERNAL - Only importable by builtins (node-internal:*)
BUNDLE - User code
Cloudflare-specific APIs
Implemented in src/cloudflare/:
// src/cloudflare/workers-types.ts
export interface Fetcher {
fetch ( request : Request | string ) : Promise < Response >;
}
export interface DurableObjectStub {
fetch ( request : Request | string ) : Promise < Response >;
id : DurableObjectId ;
}
export interface DurableObjectStorage {
get < T = unknown >( key : string ) : Promise < T | undefined >;
put < T = unknown >( key : string , value : T ) : Promise < void >;
delete ( key : string ) : Promise < boolean >;
list < T = unknown >( options ?: ListOptions ) : Promise < Map < string , T >>;
}
TypeScript definitions
Generated from C++ using JSG RTTI system:
class Response : public jsg :: Object {
JSG_RESOURCE_TYPE ( Response ) {
JSG_TS_ROOT (); // Mark as root for generation
JSG_TS_OVERRIDE (type Response = {
json < T = any >(): Promise < T > ;
});
JSG_METHOD (constructor);
// ...
}
};
Generate types:
Output: types/defines/
Compatibility flags
Conditionally enable features:
JSG_RESOURCE_TYPE (MyAPI, CompatibilityFlags ::Reader flags) {
JSG_METHOD (standardMethod);
if ( flags . getExperimentalFeature ()) {
JSG_METHOD (experimentalMethod);
}
}
Flags defined in src/workerd/io/compatibility-date.capnp:
struct CompatibilityFlags @0x... {
experimentalFeature @0 :Bool;
# Enabled by default on 2024-01-15
}
Testing APIs
Test in .wd-test files:
# my-api-test.wd-test
using Workerd = import "/workerd/workerd.capnp";
const unitTests :Workerd.Config = (
services = [(
name = "my-api-test",
worker = (
modules = [(name = "worker", esModule = embed "my-api-test.js")],
compatibilityDate = "2024-01-01",
),
)],
);
// my-api-test.js
import { strictEqual } from 'node:assert' ;
export default {
async test () {
const api = new MyAPI ();
const result = api . doSomething ();
strictEqual ( result , 'expected' );
}
} ;
Best practices
Follow Web Standards
The project has a high bar for non-standard APIs. Prefer implementing existing standards.
// GOOD: Implement standard API
class TextEncoder : public jsg :: Object {
// Match MDN/WHATWG specification
};
// AVOID: Custom non-standard API
class CustomEncoder : public jsg :: Object {
// Cloudflare-specific behavior
};
Handle errors correctly
// GOOD: Use appropriate error types
JSG_REQUIRE (value > 0 , RangeError, "Value must be positive" );
JSG_REQUIRE (key != nullptr , TypeError, "Key must be a string" );
JSG_FAIL_REQUIRE (DOMInvalidStateError, "Cannot read after closed" );
// BAD: Generic error
JSG_REQUIRE (condition, Error, "Something went wrong" );
Document behavior
class MyAPI : public jsg :: Object {
// Processes the input data and returns the result.
// Throws TypeError if data is not a string or ArrayBuffer.
// Throws RangeError if data exceeds maximum size.
jsg :: Promise < kj :: Array < byte >> process (
jsg :: Lock & js ,
kj :: OneOf < kj :: String , kj :: Array < byte >> data );
};
Use appropriate types
// GOOD: Union type for flexible input
void send ( kj :: OneOf < kj :: String , kj :: Array < byte >> data );
// GOOD: Optional parameter
void fetch ( kj :: String url , jsg :: Optional < FetchOptions > options );
// GOOD: Return promise for async
jsg :: Promise < Response > fetchAsync ( jsg :: Lock & js , kj :: String url );
Adding a new API
Discuss first - File an issue or discussion before coding
Choose location - api/ for standard APIs, api/node/ for Node.js
Implement - Follow patterns from existing APIs
Add tests - Create .wd-test file with test cases
Update docs - Add TypeScript definitions if needed
Register - Add to module registry or global scope
See docs/api-updates.md for detailed guidelines.
Next steps
Contributing overview Review contribution guidelines
Code style Learn about code style requirements