How socket.io can be used like axios? - javascript

I've an application, which is built in axios, just PUT, POST, DELETE, GET in mind. which looks like this
getAPI = axios.create(.....)
....
getAPI.post('signup/', {email, password})
.then(res => {
/// return some res
})
.catch(err => {
/// some error is show if not succeed
})
}
and also goes or "post/", "logout/", "signup/" with different methods.
Later i found that, In order to make post actions realtime in client side , we need to use websocket. So i used socket.io .
I've already setup server and client.
In server socket connection like this
io.on('connection', socket => {
console.log('User is connected on socket');
socket.on('disconnect', () => console.log('disconnected'));
})
and in client connection i've searc tutorials and used contextAPI, and passed to allcomponents.
in my specific component, where i've user post and user post is shown i've put code like this
const {socket} = useContext(AuthContext);
useEffect(() => {
socket.on("connect", () => {
console.log("client connected")
})
return ()=> socket.disconnect()
})
Now how can i use those of axios request with catch errors but with socket.io. It seems very hard to me using socket.io integrating with axios . Although i need not to use socket on authentication. But i need to use it on "/post" request.
Posting from client to server was easy by that axios.POST.then().catch(), axios.GET .....
but i'm confused to integrate that axios things in socket in client .
Also in backend side , i've routes like these
router.get('/logout', logout)
router.post('/post/create', post)
with each handler like these
exports.postCreate = (req, res) => {
let post = new Post(req.body)
post.save((err, post) => {
if(err){
return res.status(400).json({error: "Error"})
}
return res.json(post)
})
}
but if i want to use socket.io, what should i do? I'm very confused by socket.io docs, not showing for handeling things.
If you have idea about these things,
please answer me Thank you for your answer

Socket.io keeps its connections alive. In order to handle errors. You will need to listen to events. For example:
Handling connection errors:
socket.on("connect_error", (error) => {
// ...
});
Handling disconnect errors:
socket.on("disconnect", (reason) => {
if (reason === "io server disconnect") {
// the disconnection was initiated by the server, you need to reconnect manually
socket.connect();
}
// else the socket will automatically try to reconnect
});
If you'd like to ensure that your server side handled your request and need confirmation you can use the optional 'ack' feature like this:
// client side
socket.emit("ferret", "tobi", (data) => {
console.log(data); // data will be "woot"
});
// server side:
io.on("connection", (socket) => {
socket.on("ferret", (name, fn) => {
fn("woot");
});
});

Related

Callback Function in App.Listen in Nodejs after User Logs in

I am building an IOT app incorporating MQTT. My challenge is this :
The list of topics a user has subscribed to are saved to a MongoDB collection. Once the User logs into the app, this list of topics is available for operation. What I want to do is to create a function that starts running once the user has logged in and listens to any message on the subscribed topics and update device state which is also saved to the MongoDB collection.
Through this code, I can receive messages if I hard code the topics :
mongoose.connect(URI).then(res => {
console.log("Connected to DB")
const server = app.listen(3000, function () {
const client = mqtt.connect('mqtt://test.mosquitto.org')
// console.log("MQTT CLIENT : ", client)
client.on('connect', () => {
console.log('Connected')
})
client.subscribe(`62d7d71d65c27a/devices/62dd2208c4b`, () => {
console.log("Subscribe to topic 62d7d71d65c27a/devices/62dd2208c4b")
})
client.on('message', (topic, payload) => {
console.log(payload.toString())
})
const io = require('./socket').init(server)
})
}).catch(err => console.log(err))
How can I make a call back function to run with app.listen, which can be called once the user has logged in ?

Sails js client native websocket

I'm trying to use websockets with sails-js but I can't make it work with native javascript websockets.
the tutorial example use the sails.io.js library and it goes a little bit like this:
io.socket.on('hello', function (data) {
console.log('Socket `' + data.id + '` joined the party!');
});
function sendHello () {
// And use `io.socket.get()` to send a request to the server:
io.socket.get('/websockets/hello', function gotResponse(data, jwRes) {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
});
}
This does work, but i want to use the native javascript websockets like this:
let ws = new WebSocket("ws://localhost:1337/websockets/hello");
ws.onopen = function (e) {
console.log("[open] Connection established");
console.log("Sending to server");
ws.send("My name is John");
};
ws.onmessage = function (event) {
console.log(`[message] Data received from server: ${event.data}`);
};
ws.onclose = function (event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
ws.onerror = function (error) {
console.log(`[error] ${error}`);
console.log(error);
};
Clean and native javascript websockets with no library needed. Unfortunately I can't seem to make it work.
When I try to connect to sails js server using native websockets I get this message:
WebSocket connection to 'ws://localhost:1337/websockets/hello' failed: Connection closed before receiving a handshake response
Impossible to make it connect, it seems like sails js don't even get the message because i make a log when i get a new connection (using the tutorial code):
module.exports = {
hello: function (req, res) {
console.log("web socket received",req.isSocket)
// Make sure this is a socket request (not traditional HTTP)
if (!req.isSocket) {
return res.badRequest();
}
// Have the socket which made the request join the "funSockets" room.
sails.sockets.join(req, 'funSockets');
// Broadcast a notification to all the sockets who have joined
// the "funSockets" room, excluding our newly added socket:
sails.sockets.broadcast('funSockets', 'hello', { howdy: 'hi there!' }, req);
// ^^^
// At this point, we've blasted out a socket message to all sockets who have
// joined the "funSockets" room. But that doesn't necessarily mean they
// are _listening_. In other words, to actually handle the socket message,
// connected sockets need to be listening for this particular event (in this
// case, we broadcasted our message with an event name of "hello"). The
// client-side code you'd need to write looks like this:
//
// io.socket.on('hello', function (broadcastedData){
// console.log(data.howdy);
// // => 'hi there!'
// }
//
// Now that we've broadcasted our socket message, we still have to continue on
// with any other logic we need to take care of in our action, and then send a
// response. In this case, we're just about wrapped up, so we'll continue on
// Respond to the request with a 200 OK.
// The data returned here is what we received back on the client as `data` in:
// `io.socket.get('/say/hello', function gotResponse(data, jwRes) { /* ... */ });`
return res.json({
anyData: 'we want to send back'
});
}
};
How can I make sails js work with native javascript websockets?
Found a simple solution!
Used the npm package ws: npm i ws
making a new hook: sails generate hook customWebSocket
in the hook :
/**
* WS hook
*
* #description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic.
* #docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
*/
const WebSocket = require('ws');
module.exports = function defineWsHook(sails) {
return {
/**
* Runs when this Sails app loads/lifts.
*/
initialize: async function () {
sails.log.info('Initializing custom hook (`WS`)');
console.log("custom hook")
const wss = new WebSocket.Server({ port: 3100 });
wss.on('connection', (socket) => {
console.log('New user connected wss');
socket.on('message', function incoming(message) {
console.log(message)
});
});
}
};
};
Done and done, now i can connect to is using native websocket!
now that i have done that i realize that the socket.io library might be better for handling errors.

NextJS, Express, Error during WebSocket handshake: Unexpected response code: 200

The basic problem can be summarized as follows: When creating a Websocket server in Node using ws with the server option populated by an express server(as in this example), while using that same express server to handle the routing for NextJS (as in this example), the upgrade header seems to not be properly parsed.
Instead of the request being routed to the Websocket server, express sends back an HTTP 200 OK response.
I've searched high and low for an answer to this, it may be that I simply do not understand the problem. A possibly related question was brought up in an issue on NextJS's github. They recommend setting WebsocketPort and WebsocketProxyPort options in the local next.config.js, however I have tried this to no avail.
A minimal example of the relevant server code can be found below. You may find the full example here.
const express = require('express')
const next = require('next')
const SocketServer = require('ws').Server;
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
const wss = new SocketServer({ server });
wss.on('connection', function connection(ws, request) {
console.log('Client connected');
ws.on('close', () => console.log('Client disconnected'));
});
wss.on('error', function (error) {
console.log(error);
});
setInterval(() => {
wss.clients.forEach((client) => {
client.send(new Date().toTimeString());
});
}, 1000);
}).catch(ex => {
console.error(ex.stack);
process.exit(1);
});
The expected result, of course, is a connection to the websocket server. Instead I receive the following error:
WebSocket connection to 'ws://localhost:3000/' failed: Error during WebSocket handshake: Unexpected response code: 200
Can anyone elucidate anything for me here?
Ok, after more digging I have solved the problem. Quite simply, the ws.Server object to which I was trying to feed the server = express() object is not strictly speaking an http server object. However, server.listen() returns such an http server object. On such an object we can listen for an 'upgrade' call, which we can pass to our ws.Server object's handleUpgrade() event listener, through which we can connect. I will be updating the examples that I linked in my question, but the relevant code is below:
app.prepare().then(() => {
const server = express()
server.all('*', (req, res) => {
return handle(req, res)
})
const wss = new SocketServer({ server });
wss.on('connection', function connection(ws, request) {
console.log('Client connected');
ws.on('close', () => console.log('Client disconnected'));
});
wss.on('error', function (error) {
console.log(error);
});
let srv = server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
srv.on('upgrade', function(req, socket, head) {
wss.handleUpgrade(req, socket, head, function connected(ws) {
wss.emit('connection', ws, req);
})
});

Sails.js websockets work in localhost but not working after I deploy to Openshift

Summary:
I am working on an admin panel for a sails.js based website. I have configured websockets to work in my localhost. But when I deploy to openshift, only the io.socket.on('connect',...); is firing. But when I publishAdd a record, nothing is being received in the client.
Details:
Firstly some details about my configuration:
Sails.js version: 0.12.3
Node.js version: v0.10.36 (Changed it to this to match openshift's version v0.10.35)
Socket client: sails.io.js
Tried with both memory socket store and redis socket store and the behavior is the same
Current Behavior:
I am trying to enable a notification system to my application so I am using the default sails.io.js to connect a websocket (Using the default io.socket object) in my client side code I am using console.log() for now to log any events. The code looks like this in the client js file:
$(document).ready(function () {
io.sails.environment = "development";
if(window.location.hostname === "localhost") {
io.sails.url = 'http://localhost:1337';
} else {
io.sails.url = 'http://myapp-username.rhcloud.com:8000';
}
var adminSocket = io.socket;
// Access socket's connect event to initialize functions
adminSocket.on('connect', function () {
console.log("Connected to socket");
// Subscribe to admin model of current user
adminSocket.get('/admin/subscribeToModel');
});
// Access notification (adminAlerts) model's socket events
adminSocket.on('admin', function(event) {
console.log(event);
});
// Log Errors
adminSocket.on('error', function (event) {
console.log(event);
});
});
As you can see the code runs io.socket.get(...) after the socket connects. This works for both localhost and openshift and logs "Connected to socket" and also sails.io.js's default connection message. When the server turns off the client tries to reconnect as the default behavior.
io.socket.get runs a custom controller function in the admin controller:
subscribeToModel: function(req, res, next) {
if (!req.isSocket) {
console.log("Not Socket");
return res.badRequest();
}
console.log("Socket");
Admin.subscribe(req, [req.session.userid]); //Subscribes to Admin model's entry with the user's id
return res.json({message: "Subscribed"});
}
This logs "Socket" in Both openshift and localhost but the return message is not available. I don't know if subscribe works or not in openshift but it works for sure in localhost.
This is the setup. Now I built a function to test socket behavior. In admin controller I created:
createAndBroadcastNotification: function (req, res, next) {
AdminAlerts.create({
admin: req.session.admin.id,
alertHeader: 'Some Header',
alertBody: 'Some Message',
alertType: 'label-info',
url: '/somepage'
}).exec(function (err, alert) {
if (err) {
res.serverError(err);
}
Admin.findOne({ where: {id: req.session.admin.id} }).populate('alerts').exec(function (err, admin) {
if (err) {
res.serverError(err);
}
Admin.publishAdd(req.session.userid, 'alerts', alert.id);
return res.json({message: "Done"});
});
});
}
After this Both openshift and localhost are showing {message:Done} in the browser and the record is being created and associated in the mySQL database. But Only Localhost is posting this message in the console as expected:
Object {id: 1, verb: "addedTo", attribute: "alerts", addedId: 10}
Openshift is showing no such message. Neither is it showing any error messages.
I have been trying to figure out the problem for 5 days. I have not been able to find a clue as to why this is happening.
Objective:
I wish to get the console.log() message in the client to signify an alert being added.
I figured it out. For anyone facing the same or similar issue, the documentation for openshift (In the blog) is outdated. Letting sails.io.js figure out the host and port works "just fine". It returns a connection error in firefox though. Here are the changes:
// client-side-code.js
$(document).ready(function () {
// io.sails.environment = "development";
// if(window.location.hostname === "localhost") {
// io.sails.url = 'http://localhost:1337';
// } else {
// io.sails.url = 'http://myapp-username.rhcloud.com:8000';
// }
var adminSocket = io.socket;
// Access socket's connect event to initialize functions
adminSocket.on('connect', function () {
console.log("Connected to socket");
// Subscribe to admin model of current user
adminSocket.get('/admin/subscribeToModel');
});
// Access notification (adminAlerts) model's socket events
adminSocket.on('admin', function(event) {
console.log(event);
});
// Log Errors
adminSocket.on('error', function (event) {
console.log(event);
});
});

socket.io keep repeating fail request

I have strange issue. Basically the client will send a request i.e:delete chat, but Server will reject, since client is not authorized.
however, the client keep repeating the same request, even if I open new browser and load the same address. Both browser will keep requesting the previous action. my code looks something like this:
Client
socket.on(username, (res) => {
show(res.err)
})
socket.send({
type: "delete_chat",
username: username,
id: chat_id
})
Server:
io.sockets.on("connection", (socket) => {
socket.on("message", (data) => {
if(data.type === "delete_chat"){
Chat.delete(chatid, (err, res) => {
io.emit(username, {err:res}) //send error to username
});
}
})
})

Categories