Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"ReferenceError: WebSocket is not defined" when using RxJs WebSocketSubject and Angular Universal

I'm setting up an angular 6.x univeral project in order to leverage its SSR (Server-Side Rendering) capabilities. In my app, I'm using websocket communication using RxJs.

More specifically, I'm, using WebSocketSubject and webSocket in my angular universal 6.x project, which works fine on the browser platform. However, when running the node web server (that contains the SSR stuff (Server-Side Rendering)), an error is thrown:

ReferenceError: WebSocket is not defined

Example code:

// not actually code from the reproduction repo
import { WebSocketSubject, webSocket } from 'rxjs/webSocket';

const socket: WebSocketSubject<any> = webSocket('wss://echo.websocket.org');
socket.subscribe(msg => doSomething(msg));

Please note, that the error doesn't occur on the browser version of the app (i.e. ng serve won't throw the error), but only after compiling the SSR stuff and running the express web server. To reproduce the error, the builds have to be run first:

# install dependencies
npm install

# build angular bundles for browser and server platforms
npm run build:client-and-server-bundles

# build the webserver
npm run webpack:server

# start the webserver
npm run serve:ssr

# the app is now being served at http://localhost:8081/
# open it in the browser and the error will occur: 'ReferenceError: WebSocket is not defined'

I've also set up a reproduction repo.

Environment I'm using:

  • Runtime: Angular 6
  • RxJS version: 6.2.0, 6.2.1, 6.2.2 (tested all)

Edit 2018-08-02

I was able to address the problem more accurately. It seems, that this is also a webpack problem. Angular Universal creates a js bundle for running the angular app on the node server, but there is natively no websocket implementation on Node. Therefore, it has to be added manually as a dependency (npm package). I tried adding it to the js server bundle (const WebSocket = require('ws'); manually, which resolves the problem (i.e. ReferenceError disappears). However, when I add it to the TypeScript code that gets transcompiled into the js bundle later on, it won't work.

Further details

  • The webpack loader ts-loader is used for compiling TypeScript => JavaScript
  • The websocket depedency was added to the package.json: "ws": "^6.0.0"
  • Attempting to reference the ws dependency by adding const WebSocket = require('ws'); to the uncompiled TypeScript code won't resolve the issue. It would get compiled into var WebSocket = __webpack_require__(333); in the js output file, the dependency won't be able to be resolved.
  • Manually changing var WebSocket = __webpack_require__(333); => const WebSocket = require('ws'); in the compiled js file would resolve the issue, but of course it's a hack.

So, the questions are:

  • Can I "force" webpack to compile the dependency const WebSocket = require('ws'); => const WebSocket = require('ws'); (no changes)?
  • Or, would it might be a better solution, to register this dependency in the angular universal npm package and create a pull request?
like image 547
Ronin Avatar asked Jul 26 '18 06:07

Ronin


3 Answers

In nodeJS apps with ES6 or similar, you must use this:

global.WebSocket = require('ws')

For example:

import { WebSocketSubject, webSocket } from 'rxjs/webSocket';

global.WebSocket = require('ws'); // <-- FIX ALL ERRORS
const subject = webSocket('ws://...');

No more errors.

My first answer!! Cheers

like image 67
Emi Santi Avatar answered Nov 09 '22 18:11

Emi Santi


The RxJS webSocket function can receive a WebSocket Constructor as an argument. This can be used to run webSocket in a non-browser/universal environment like this.

const WebSocketConstructor = WebSocket || require('ws')

const socket: WebSocketSubject<any> = webSocket({
    url: 'wss://echo.websocket.org', 
    WebSocketCtor: WebSocketConstructor,
});


// OR...

import { WebSocket, ...otherStuff... } from 'ws';
import { webSocket, ...otherStuff... } from 'rxjs/webSocket';

const socket$ = webSocket({
  url: 'wss://someplace.com',
  WebSocketCtor: WebSocket,
});
like image 7
Bas Avatar answered Nov 09 '22 18:11

Bas


All it takes is this:

// set WebSocket on global object
(global as any).WebSocket = require('ws');

And of course, we need the ws dependency as well in the package.json:

npm i ws -s

like image 6
Ronin Avatar answered Nov 09 '22 18:11

Ronin