Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/workerd/llms.txt

Use this file to discover all available pages before exploring further.

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:
just 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

  1. Discuss first - File an issue or discussion before coding
  2. Choose location - api/ for standard APIs, api/node/ for Node.js
  3. Implement - Follow patterns from existing APIs
  4. Add tests - Create .wd-test file with test cases
  5. Update docs - Add TypeScript definitions if needed
  6. 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

Build docs developers (and LLMs) love