Loading…
Over the impending months, Cloudflare Workers will start up to roll out built-in compatibility with Node.js core APIs as section of an effort to enhance increased compatibility sooner or later of JavaScript runtimes.
We are chuffed to roar at the moment that the first of these Node.js APIs – AsyncLocalStorage, EventEmitter, Buffer, speak, and parts of util – are now out there to be used. These APIs are offered directly by the open-source Cloudflare Workers runtime, with out a deserve to bundle polyfill implementations into your have code.
These fresh APIs will likely be found in at the moment — start the utilize of them by enabling the nodejs_compat compatibility flag in your Workers.
Async Context Tracking with the AsyncLocalStorage APIThe AsyncLocalStorage API offers a solution to trace context sooner or later of asynchronous operations. It capacity that you just can cross a payment by your program, even sooner or later of more than one layers of asynchronous code, with out having to cross a context payment between operations.
Accumulate into consideration an instance where we want to add debug logging that works by more than one layers of an utility, where each log consists of the ID of the latest ask. With out AsyncLocalStorage, it could well be most well-known to explicitly cross the ask ID down by each function call that can perchance perchance invoke the logging function:
function logWithId(identity, direct) {
console.log(`${identity} – ${direct}`);
}
function doSomething(identity) {
// We manufacture no longer genuinely utilize identity for anything in this function!
// Or no longer it’s handiest here because logWithId needs it.
logWithId(identity, “doing something”);
setTimeout(()=> doSomethingElse(identity), 10);
}
function doSomethingElse(identity) {
logWithId(identity, “doing something else”);
}
let idSeq=0;
export default {
async derive(req) {
const identity=idSeq++;
doSomething(identity);
logWithId(identity, ‘total’);
return fresh Response(“good ample”);
}
}
While this implies works, it could perchance even be cumbersome to coordinate precisely, especially because the complexity of an utility grows. The utilization of AsyncLocalStorage this becomes greatly more straightforward by laying aside the deserve to explicitly cross the context around. Our utility functions (doSomething and doSomethingElse in this case) by no formulation deserve to snatch relating to the ask ID in any appreciate whereas the logWithId function does exactly what we would to find it irresistible to:
import { AsyncLocalStorage } from ‘node:async_hooks’;
const requestId=fresh AsyncLocalStorage();
function logWithId(direct) {
console.log(`${requestId.getStore()} – ${direct}`);
}
function doSomething() {
logWithId(“doing something”);
setTimeout(()=> doSomethingElse(), 10);
}
function doSomethingElse() {
logWithId(“doing something else”);
}
let idSeq=0;
export default {
async derive(req) {
return requestId.whisk(idSeq++, ()=> {
doSomething();
logWithId(‘total’);
return fresh Response(“good ample”);
});
}
}
With the nodejs_compat compatibility flag enabled, import statements are broken-down to access particular APIs. The Workers implementation of these APIs requires the utilize of the node: specifier prefix that became launched honest no longer too long in the past in Node.js (e.g. node:async_hooks, node:events, etc)
We put into effect a subset of the AsyncLocalStorage API in notify in confidence to place issues as straight forward as that you just would judge. Particularly, we’ve chosen now to not enhance the enterWith() and disable() APIs that are learned in Node.js implementation simply because they salvage async context monitoring more brittle and error inclined.
Conceptually, at any given 2nd inner a worker, there is a newest “Asynchronous Context Frame”, which consists of a map of storage cells, each maintaining a retailer payment for a particular AsyncLocalStorage instance. Calling asyncLocalStorage.whisk(…) causes a brand fresh frame to be created, inheriting the storage cells of the latest frame, however the utilize of the newly offered retailer payment for the cell connected to asyncLocalStorage.
const als1=fresh AsyncLocalStorage();
const als2=fresh AsyncLocalStorage();
// Code here runs in the root frame. There are two storage cells,
// one for als1, and one for als2. The retailer payment for each is
// undefined.
als1.whisk(123, ()=> {
// als1.whisk(…) creates a brand fresh frame (1). The retailer payment for als1
// is decided to 123, the retailer payment for als2 is aloof undefined.
// This fresh frame is decided to “newest”.
als2.whisk(321, ()=> {
// als2.whisk(…) creates one other fresh frame (2). The retailer payment
// for als1 is aloof 123, the retailer payment for als2 is decided to 321.
// This fresh frame is decided to “newest”.
console.log(als1.getStore(), als2.getStore());
});
// Frame (1) is restored because the latest. The retailer payment for als1
// is aloof 123, however the retailer payment for als2 is undefined again.
});
// The foundation frame is restored because the latest. The retailer values for
// each als1 and als2 are each undefined again.
Every time an asynchronous operation is initiated in JavaScript, as an illustration, creating a brand fresh JavaScript promise, scheduling a timer, etc, the latest frame is captured and connected to that operation, allowing the retailer values for the time being the operation became initialized to be propagated and restored as wanted.
const als=fresh AsyncLocalStorage();
const p1=als.whisk(123, ()=> {
return promise.unravel(1).then(()=> console.log(als.getStore());
});
const p2=promise.unravel(1);
const p3=als.whisk(321, ()=> {
return p2.then(()=> console.log(als.getStore()); // prints 321
});
als.whisk(‘ABC’, ()=> setInterval(()=> {
// prints “ABC” to the console once a 2nd…
setInterval(()=> console.log(als.getStore(), 1000);
});
als.whisk(‘XYZ’, ()=> queueMicrotask(()=> {
console.log(als.getStore()); // prints “XYZ”
}));
Expose that for unhandled promise rejections, the “unhandledrejection” event will mechanically propagate the context that is expounded to the promise that became rejected. This habits is a ramification of from a ramification of kinds of events emitted by EventTarget implementations, which will propagate whichever frame is newest when the event is emitted.
const asyncLocalStorage=fresh AsyncLocalStorage();
asyncLocalStorage.whisk(123, ()=> Promise.reject(‘growth’));
asyncLocalStorage.whisk(321, ()=> Promise.reject(‘boom2’));
addEventListener(‘unhandledrejection’, (event)=> {
// prints 123 for the first unhandled rejection (‘growth’), and
// 321 for the 2nd unhandled rejection (‘boom2’)
console.log(asyncLocalStorage.getStore());
});
Workers can utilize the AsyncLocalStorage.snapshot() formulation to notice their very have objects that rob and propagate the context:
const asyncLocalStorage=fresh AsyncLocalStorage();
class MyResource {
#runInAsyncFrame=AsyncLocalStorage.snapshot();
doSomething(…args) {
return this.#runInAsyncFrame((…args)=> {
console.log(asyncLocalStorage.getStore());
}, …args);
}
}
const resource1=asyncLocalStorage.whisk(123, ()=> fresh MyResource());
const resource2=asyncLocalStorage.whisk(321, ()=> fresh MyResource());
resource1.doSomething(); // prints 123
resource2.doSomething(); // prints 321
For more, discuss over with the Node.js documentation relating to the AsyncLocalStorage API.
There could be at the moment an effort underway to add a brand fresh AsyncContext mechanism (impressed by AsyncLocalStorage) to the JavaScript language itself. While it is aloof early days for the TC-39 proposal, there could be appropriate reason to rely on it to growth by the committee. As soon as it does, we seek forward to being in a direct to salvage it out there in the Cloudflare Workers platform. We rely on our implementation of AsyncLocalStorage to be admire minded with this fresh API.
The proposal for AsyncContext offers an very honest appropriate predicament of examples and description of the inducement of why async context monitoring is important.
Events with EventEmitterThe EventEmitter API is thought to be one of the most classic Node.js APIs and is necessary to supporting many different increased level APIs, collectively with streams, crypto, fetch, and more. An EventEmitter is an object that emits named events that trigger listeners to be known as.
import { EventEmitter } from ‘node:events’;
const emitter=fresh EventEmitter();
emitter.on(‘hiya’, (…args)=> {
console.log(…args);
});
emitter.emit(‘hiya’, 1, 2, 3);
The implementation in the Workers runtime fully helps the total Node.js EventEmitter API collectively with the captureRejections choice that allows improved handling of async functions as event handlers:
const emitter=fresh EventEmitter({ captureRejections: honest });
emitter.on(‘hiya’, async (…args)=> {
throw fresh Error(‘growth’);
});
emitter.on(‘error’, (err)=> {
// the async promise rejection is emitted here!
});
Please discuss over with the Node.js documentation for more small print on the utilize of the EventEmitter API: https://nodejs.org/dist/newest-v19.x/medical doctors/api/events.html#events.
BufferThe Buffer API in Node.js predates the introduction of the celebrated TypedArray and DataView APIs in JavaScript by a few years and has continued as thought to be one of the most veritably broken-down Node.js APIs for manipulating binary information. As of late, each Buffer instance extends from the celebrated Uint8Array class but adds a differ of uncommon capabilities equivalent to built-in harmful64 and hex encoding/decoding, byte-notify manipulation, and encoding-acutely conscious substring procuring.
import { Buffer } from ‘node:buffer’;
const buf=Buffer.from(‘hiya world’, ‘utf8’);
console.log(buf.toString(‘hex’));
// Prints: 68656c6c6f20776f726c64
console.log(buf.toString(‘harmful64’));
// Prints: aGVsbG8gd29ybGQ=
Because a Buffer extends from Uint8Array, it could perchance even be broken-down in any workers API that at the moment accepts Uint8Array, equivalent to creating a brand fresh Response:
const response=fresh Response(Buffer.from(“hiya world”));
Or interacting with streams:
const writable=getWritableStreamSomehow();
const creator=writable.getWriter();
creator.write(Buffer.from(“hiya world”));
Please discuss over with the Node.js documentation for more small print on the utilize of the Buffer API: https://nodejs.org/dist/newest-v19.x/medical doctors/api/buffer.html.
AssertionsThe speak module in Node.js offers a assortment of important assertions that are important when constructing checks.
import {
strictEqual,
deepStrictEqual,
good ample,
doesNotReject,
} from ‘node:speak’;
strictEqual(1, 1); // good ample!
strictEqual(1, “1”); // fails! throws AssertionError
deepStrictEqual({ a: { b: 1 }}, { a: { b: 1 }});// good ample!
deepStrictEqual({ a: { b: 1 }}, { a: { b: 2 }});// fails! throws AssertionError
good ample(honest); // good ample!
good ample(flawed); // fails! throws AssertionError
look forward to doesNotReject(async ()=> {}); // good ample!
look forward to doesNotReject(async ()=> { throw fresh Error(‘growth’) }); // fails! throws AssertionError
In the Workers implementation of speak, all assertions whisk in what Node.js calls the “strict assertion mode”, which formulation that non-strict strategies behave admire their corresponding strict strategies. As an illustration, deepEqual() will behave admire deepStrictEqual().
Please discuss over with the Node.js documentation for more small print on the utilize of the assertion API: https://nodejs.org/dist/newest-v19.x/medical doctors/api/speak.html.
Promisify/CallbackifyThe promisify and callbackify APIs in Node.js present one plan of bridging between a Promise-based programming model and a callback-based model.
The promisify formulation permits taking a Node.js-model callback function and changing it into a Promise-returning async function:
import { promisify } from ‘node:util’;
function foo(args, callback) {
strive {
callback(null, 1);
} utilize (err) {
// Errors are emitted to the callback by plan of the first argument.
callback(err);
}
}
const promisifiedFoo=promisify(foo);
look forward to promisifiedFoo(args);
Similarly, callbackify converts a Promise-returning async function into a Node.js-model callback function:
import { callbackify } from ‘node:util’;
async function foo(args) {
throw fresh Error(‘growth’);
}
const callbackifiedFoo=callbackify(foo);
callbackifiedFoo(args, (err, payment)=> {
if (err) throw err;
});
Collectively these utilities salvage it easy to smartly take care of the total veritably troublesome nuances alive to with smartly bridging between callbacks and promises.
Please discuss over with the Node.js documentation for more information on how one can utilize these APIs: https://nodejs.org/dist/newest-v19.x/medical doctors/api/util.html#utilcallbackifyoriginal, https://nodejs.org/dist/newest-v19.x/medical doctors/api/util.html#utilpromisifyoriginal.
Form price-checking with util.typesThe util.forms API offers a legit and veritably more ambiance kindly plan of checking that values are cases of a ramification of built-in forms.
import { forms } from ‘node:util’;
forms.isAnyArrayBuffer(fresh ArrayBuffer()); // Returns honest
forms.isAnyArrayBuffer(fresh SharedArrayBuffer()); // Returns honest
forms.isArrayBufferView(fresh Int8Array()); // honest
forms.isArrayBufferView(Buffer.from(‘hiya world’)); // honest
forms.isArrayBufferView(fresh DataView(fresh ArrayBuffer(16))); // honest
forms.isArrayBufferView(fresh ArrayBuffer()); // flawed
function foo() {
forms.isArgumentsObject(arguments); // Returns honest
}
forms.isAsyncFunction(function foo() {}); // Returns flawed
forms.isAsyncFunction(async function foo() {}); // Returns honest
// .. and loads others
Please discuss over with the Node.js documentation for more information on how one can utilize the variety check APIs: https://nodejs.org/dist/newest-v19.x/medical doctors/api/util.html#utiltypes. The workers implementation at the moment does no longer present implementations of the util.forms.isExternal(), util.forms.isProxy(), util.forms.isKeyObject(), or util.variety.isWebAssemblyCompiledModule() APIs.
What’s nextKeep your eyes open for more Node.js core APIs coming to Cloudflare Workers soon! We at the moment own implementations of the string decoder, streams and crypto APIs in appealing type. These will likely be launched into the workers runtime incrementally over time and any worker the utilize of the nodejs_compat compatibility flag will mechanically make a selection up the fresh modules as they’re added.
We provide protection to
complete company networks,
inspire possibilities build
Web-scale functions successfully,
tempo up any
internet internet site
or Web utility,
beat again DDoS
attacks, retain
hackers at
bay,
and can enable you on
your slide to Zero Belief.
Search the advice of with 1.1.1.1 from any gadget initially
our free app that makes your Web sooner and safer.
To be taught more about our mission to inspire build a better Web, start here. Whenever you are procuring for a
fresh profession route, investigate cross-check our open
positions.
Node.js
Cloudflare Workers
JavaScript
WinterCG