WebSocket interface cannot be used in place of WebSocket? - node.js

I'm following a guide to try and set up a WebSocket.Server using ws and Express 4 with NodeJS and TypeScript. Problem is that the guide I'm following (found here: https://morioh.com/p/3b302785a62f) seems like it's out of date or something because the code provided doesn't work.
I'm trying to use an extended websocket object to keep track of an alive connection. It looks like this:
interface ExtWebSocket extends WebSocket {
isAlive: boolean;
}
Now doing this, as the code in the tutorial shows, I get an error:
setInterval(() => {
wss.clients.forEach((ws: ExtWebSocket) => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping(null, false, true);
});
}, 10000);
The error states that I cannot use the ExtWebSocket in the forEach loop but I cannot figure out why?

It is because you can't 'de-narrow' a WebSocket to an ExtWebSocket since it extends WebSocket. If you had an ExtWebSocket, then you could narrow it down to a WebSocket but not vice-versa.
Here is a link to the typescript handbook on this topic: https://www.typescriptlang.org/docs/handbook/2/narrowing.html

Related

Why is received websocket data coming out as a buffer?

I'm trying to do a very basic websocket, but i dont understand why I'm not getting a string back.
I'm using the ws module from npm for the server. https://github.com/websockets/ws
client:
let socket = new WebSocket('wss://upload.lospec.com');
socket.addEventListener('open', function (event) {
socket.send('test');
});
server:
const wss = new WebSocket.Server({ server });
wss.on("connection", function (ws) {
ws.on("message", function (asdfasdf) {
console.log("got new id from client",asdfasdf);
});
server result:
got new id from client <Buffer 74 65 73 74>
Trying to follow the examples on the docs, as well as this tutorial: https://ably.com/blog/web-app-websockets-nodejs
But it's not coming out like a string like both places promise.
Why isn't this coming out as a string?
You probably use a different version of ws than the tutorial does. It seems like the tutorial uses a version older than v8, while you use a version of v8+.
From the changelog for 8.0.0:
Text messages and close reasons are no longer decoded to strings. They are passed as Buffers to the listeners of their respective events.
The listeners of the 'message' event now take a boolean argument specifying whether or not the message is binary (e173423).
Existing code can be migrated by decoding the buffer explicitly.
websocket.on('message', function message(data, isBinary) {
const message = isBinary ? data : data.toString();
// Continue as before.
});
websocket.on('close', function close(code, data) {
const reason = data.toString();
// Continue as before.
});
This also describes the solution.
Alternatively, you can downgrade to version 7.5.0 of ws to be on par with what the tutorial uses:
npm i ws#7.5.0
In regards to the example "sending and receiving text data" in the library docs: I believe it's an oversight on their end that this example wasn't updated when v8 was released. You could open an issue on GitHub to let them know.
If you use console.log(` ${data} `), it will not show <Buffer.
where as if you console.log(data), it will show <Buffer
e.g.
ws.on('message',data=>{
console.log(`user sended:${data}`)
})
It's a bug or normal you can say.
alter method:
ws.on('message',data=>{
data=data.toString()
console.log("user sended:",data)
})
OR
ws.on('message',data=>{
console.log("user sended:",data.toString())
})
In Latest version one need to provide %s while printing the received message. Here is the simple code snippet from official page npm websocket official page.
ws.on('message', function message(data) {
console.log('received: %s', data);
});
I was having same issue. I found the issue was because of version of ws.
So, I installed another version of ws.
Try this instead:
npm i ws#7.5.0
It worked in my case.

Testing http.Server.close error in NodeJS

I have a code in NodeJS responsible to close an http.Server connection and I would like to test the error scenario on the http.Server.close() method.
The problem is to do that, I need to simulate the return of the close method with the err object populated, and I don't know how to do it.
Below you can find my code and I would like to test the line where we can find the code reject(err);
Note: In my integration tests, I'm starting temp HTTP servers to simulate the real scenario. So as far as I understood I need to find a real scenario where the .close method will be rejected by the default implementation.
Thanks.
this._httpServer = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => this.handler(req, res));
...
disconnect(): Promise<void> {
return new Promise((resolve, reject) => {
this._httpServer.close((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
I found the answer.
Based on the official documentation, the unique error scenario is when we are trying to close an already closed server.
So, to make the test work, before calling my disconnect method I only need to close the httpServer (this._httpServer.close()).
Reference: https://nodejs.org/docs/latest-v12.x/api/net.html#net_server_close_callback

AngularJS Mongoose error handling

I've build a simple application with AngularJS. Part of this application is calling REST services. For that, I'm using mongoose. Everything works great, but I'd like to better handle errors. A sample code could be :
Express:
DBCollection.find({}, function (err, tuples) {
if (err) {
console.log('Error!');
}
res.send(JSON.stringify(tuples));
});
AngularJS:
DBService.query(function (res) {
$scope.data.lists = res;
});
The problem I'm faced with is as follow. Imagine I've got an error on the mongodb server side. I've got an error, so I log it in the console and then ? What happens on the angularjs/front-end side ? If I send the error as a http response, I suppose angular would interpreate it as the response of the query but with unexpected content and produce an exception ? how to deal with that ?
Angular is like Santa, it knows when responses are bad or good. There are 2 solutions, ones solutions is to create an error handler on each request. The other is to use $httpProvider.interceptors to globally handle errors before they become a problem on an individual request level.
Option 1
DBService.query(function (res) {
scope.data.lists = res;
},function(errorResult){
console.log(errorResult); // <- take a peek in here, find something useful
});
Option 2
$httpProvider.interceptors.push(['$q',function($q) {
return {
'responseError': function(rejection) {
console.log(rejection); // <- take a peek in here, find something useful
return $q.reject(rejection);
}
}
}]);

How to capture the error to redis in node.js

In my application im using redis database with node.js.Now i want to build error capture module .How can i capture error in redis.Using Airbrake(https://github.com/felixge/node-airbrake) we can capture error but how can i do that on my own using redis in node.js.
If you look at the code for node-airbrake, you'll notice this:
Airbrake.prototype.handleExceptions = function() {
var self = this;
process.on('uncaughtException', function(err) {
self._onError(err, true)
});
};
So, you could implement your own uncaughtException handler (although some say that doing so is bad form, but that's another discussion) and store your errors in Redis.

Custom Events in Node.js with Express framework

So, I'd like to know how to create custom events in node.js, and I'm hitting a wall. I'm pretty sure I'm misunderstanding something about how express works and how node.js events work.
https://creativespace.nodejitsu.com That's the app.
When a user creates a new "activity" (something that will happen many times) they send a POST request. Then within my route, if that POST succeeds I'd like to emit an event, that tells socket.io to create a new namespace for that activity.
In my route file:
var eventEmitter = require('events').EventEmitter;
// Tell socket.io about the new space.
eventEmitter.emit('new activity', {activityId: body.id});
And socket.io:
// When someone creates a new activity
eventEmitter.on('new activity', function (data) { // this gives and error
var newActivity = '/activity?' + data.activityId;
io.of(newActivity).on('connection', function (socket) {
// Socket.io code for an activity
});
});
So the error I get is CANNOT CALL METHOD ON OF UNDEFINED and it refers to what would be line 2 in the socket.io above. I think I'm messing up my requires, maybe...or I'm not quite understanding how events work.
Any help, even a reference to good reading on Node.js events would rock!
Thanks!!!
If using express you can also just listen for event on the express 'app' which inherits from EventEmitter. For example:
res.app.on("myEvent", function)
and emit to it like
res.app.emit("myEvent", data)
You should treat EventEmitter as a class you can inherit from. Try this:
function MyEmitter () {
events.EventEmitter.call(this);
}
util.inherits(MyEmitter, events.EventEmitter);
Now you can use your class to listen and emit events:
var e = new MyEmitter;
e.on("test", function (m) { console.log(m); });
e.emit("test", "Hello World!");

Resources