ably-js icon indicating copy to clipboard operation
ably-js copied to clipboard

Fix "setImmediate is not supported" errors when using Realtime client in the Vercel Edge runtime

Open VeskeR opened this issue 10 months ago • 0 comments

Currently, when trying to use the Ably.Realtime instance in the Vercel Edge runtime

  • whether within serverless functions or in SSR when rendering the frontend in Edge runtime - we get the error Error: A Node.js API is used (setImmediate) which is not supported in the Edge Runtime.. This error either prevents the serverless function from running or results in a white screen of death when rendering the frontend page.

The issue comes from the assignment of the setImmediate function to the IPlatformConfig.nextTick method in the web platform bundle. We do this when setImmediate function is defined on the global object on the next lines. Consequently, IPlatformConfig.nextTick is called in various transport and websocket-related classes like Connection and WebSocketTransport during the regular Ably.Realtime workflow, leading to the error above.

Here's exactly what's happening:

  1. When importing a package in the Vercel Edge runtime, Vercel selects a browser bundle from the available bundles provided by the package in node_modules, as explained by a Vercel staff member in this GitHub issue:

So you are correct in that we decided to pick the browser field for the edge runtime, as we felt it was closer to what the runtime was capable of than Node.js (since you cannot use Node.js APIs in Edge, and browsers are usually fully spec-compliant also, compared to polyfills/packages.)

For ably-js, this means that in Vercel Edge runtimes, our web platform bundle will be selected, which includes the optional usage of the setImmediate function.

  1. The Vercel Edge runtime defines setImmediate function instead of leaving it undefined. It sets it to be the function that just throws Error: A Node.js API is used (setImmediate) which is not supported in the Edge Runtime. error immediately when called.
  2. Since setImmediate function is actually defined, no polyfill will work out of the box, as polyfills usually rely on condition checks like if (!global.setImmediate) { // define polyfill }. For example, this popular setImmediate polyfill won't work out of the box.

[!NOTE] Even if we were to forcibly polyfill the setImmediate function using this polyfill, it would still default to using the setTimeout function, as other preferred browser APIs (postMessage and MessageChannel) are also unavailable in the Vercel Edge runtime.

  1. Because setImmediate function is actually defined, our web bundle code sets IPlatformConfig.nextTick to the value of the setImmediate function, which eventually throws an error.

Ideally, we should use the following condition to check if we are running in the Vercel Edge runtime: const isVercelEdgeRuntime = typeof EdgeRuntime === 'string'; (source). Then, in our web platform bundle, we should set IPlatformConfig.nextTick to setTimeout if isVercelEdgeRuntime === true.

However, we are currently unable to implement this due to the following issue with using Ably.Realtime in the Vercel Edge runtime: https://github.com/ably/ably-js/issues/1731

The next best option is to add a Vercel Edge runtime check to the Realtime class constructor or Realtime.connect() method and throw a more readable error explaining the issues with running the Ably.Realtime instance in the Vercel Edge runtime.

┆Issue is synchronized with this Jira Story by Unito

VeskeR avatar Apr 15 '24 01:04 VeskeR