Current Behavior
I use 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
Reproduction
Example code:
import { WebSocketSubject, webSocket } from 'rxjs/webSocket';
const socket: WebSocketSubject<any> = webSocket('wss://echo.websocket.org');
socket.subscribe(msg => doSomething(msg));
Expected behavior
No error
Environment
# 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'
Additional info
Likely this should be ... && typeof WebSocket !== 'undefined' instead.
@trotyl Thanks for your suggestion.
I tried to fix it by:
if (!config.WebSocketCtor && typeof WebSocket !== 'undefined') { rm -rf node_modules/ && npm installHowever, when checking rxjs/src/internal/observable/dom/WebSocketSubject.ts in my node_modules, it doesn't contain my changes... why?
I decided to manually change the file within node_modules and running the build commands, but it still throws me the error ReferenceError: WebSocket is not defined when accessing the running app...
At least you should tell which line of code (on which project) is causing this.
@trotyl WebSocketSubject is used here:
Please note that the error only occurs after
npm run build:client-and-server-bundles) npm run webpack:server)npm run serve:ssr)The webserver bundle is generated using this webpack config:
https://github.com/takahser/angular-universal-ssr-with-rxjs-websocket/blob/master/webpack.server.config.js
Please let me know, if you need more information
@trotyl update:
if (!config.WebSocketCtor && WebSocket) { => if (!config.WebSocketCtor && typeof WebSocket !== 'undefined') {Error: no WebSocket constructor can be foundnow, looking at https://github.com/takahser/rxjs/commit/00d144092d421d00080a7b44c3672af2b4f3733d#diff-17e027a1b28f1cfb2d7a472e6a95ddb8R95, is this really correct? we assign the config.WebSocketCtor attribute to the WebSocket type. This is wrong, isn't it?
Instead, it should call the WebSocket constructor, similar to the specs: https://github.com/ReactiveX/rxjs/blob/2db0788eaeebe102c86af0132a985c131855ec67/spec/observables/dom/webSocket-spec.ts#L647
@takahser Could you solve that problem?
@Zaniyar No, but it seems as this is rather a problem with angular universal and webpack than RxJs. When using SSR, Angular Universal creates a 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. However, when I add it to the typescript code that gets transcompiled into the js bundle later on, it won't work.
I'm trying to get WebSocketSubject working on my nestjs (express framework) backend. I'm getting the same error:
ReferenceError: WebSocket is not defined
at new WebSocketSubject (.../node_modules/rxjs/src/internal/observable/dom/WebSocketSubject.ts:94:36)
I know WebSocketSubject depends on the dom/browser's native websocket library. I've made sure that "dom" is included in my tsconfig.
"ws": "6.1.0" and "rxjs": "6.3.3" are also installed.
I saw your post on stack overflow. Adding this line to the file I was using WebSocketSubject in fixed my problem:
// tslint:disable-next-line
(global as any).WebSocket = require('ws');
But, looks like a hack to me. Anyone know what is the root of this problem?
Since webSocketSubject is a wrapper around websocket object from browsers, it makes perfectly sense not to be available in the server. No one browser APIs are available when using Angular/rxjs in the server.
@CoreyCole exactly.
When running JavaScript in the browser, the JavaScript API provides us with various global objects, such as window, document, or WebSocket, which are not available on the server-side node implementation.
When running JS on the server (node), such as it is the case with Angular Prerendering, certain global browser web APIs won't be available. Therefore, we need to use certain npm packages, that provides us with the lacking functionality (such as ws in this case). When using JS, this would be sufficient, but TypeScript will complain, because the types on the WebSocket object are mising. Finally, we can convert the untyped object to the type any, which can be (as the name implies) anything ((global as any).WebSocket = require('ws');). This way, we can satisfy the TypeScript compiler. :)
You can find more examples global variables, that are different on the browser and server implementations here.
Alright, for the record, to solve this issue, cast your WebSocket object to any:
// tslint:disable-next-line
(global as any).WebSocket = require('ws');
See also on the stackoverflow post
Most helpful comment
Since
webSocketSubjectis a wrapper aroundwebsocketobject from browsers, it makes perfectly sense not to be available in the server. No one browser APIs are available when using Angular/rxjs in the server.