Using socket.io with sails js - node.js

While there used to be very good documentation for using sockets, thanks to Irl Nathon's Sails Cast series. Things have changed in v0.11, with the sails team wrapping and burying the socket.io routines.
The sails site e.g. SailsSocket is maddeningly concise, saying what to do, but not how or where to do it, or if I need to npm or bower something. This has been particularly frustrating trying to use the sails.config.sockets talked about on the sails site. Which I cannot even find in my v0.11 directories.
First, I would like to know how and where to create my own response to a io.socket.get or .post or whatever. Right now when I do a get with something like:
`io.socket.request({
method: 'get',
url: '/sites/2',
params: {},
headers: {}
},function serverResponded(body, JWR){console.log("Body: ", JSON.stringify(body,null, 4)); console.log(' JWR: ', JWR.body)});'
I get back:
undefined
VM1149:7 "Not implemented in core yet"
VM1149:7 JWR: Not implemented in core yet
I can see the sites being called in the sails console, but nothing comes across.
I believe it is because I have defined my own routes and have my own find: function in my site controller and I manually need to push something into the server side socket. But I am confused as to how I am to call a whole page with HTTP and just the tables with socket.io in the same controller routine.
Where do I write my own low level socket.io routines that can be called from a web page?
Do I still do it in the app.js file?
Sails Cast showed it being done there, but again things have changed.

Sails "virtual requests" (what they call these socket.io-based HTTP-ish request) are generally used to retrieve or post JSON data to the server. Additionally, if a client-side script makes a virtual request, the server may add or remove the requesting socket to/from rooms.
Note that using a "virtual method" will ultimately run the same controller action, but will set req.isSocket = true.
This example is a view that renders a view for HTML-wanting requests but returns JSON data for socket-based requests:
...
// 'get /sites/:id': 'SomeController.showSite' (should be put in your `routes.js`)
showSite: function(req, res) {
// load something from the database
Site.findOne(req.param('id')).exec(function(err, site) {
// handler errors (same for HTTP or sockets)
if (err) return res.serverError();
if (!site) return res.notFound();
if (req.isSocket) return res.json(site); // render JSON response for our `site` object
else return res.view('sites/show', {site: site}); // render an HTML view
});
}
As for low-level socket.io, sails provides the global variable io (from sails.io.js), which is an instance of SailsSocket. It allows you to make HTTP-ish "virtual requests". More info here (although it seems you have already read all there is to read about SailsSocket :). You can access the underlying socket.io client with io.socket._raw.
// do this in the browser.
// sails.io.js should be included in layout.ejs by default.
io.socket.get('/site/2', console.log); // "virtual request"
// neat little trick ^^^^^^^^^^^ for testing :)
var rawIO = io.socket._raw;
rawIO.emit('some:event', "using native socket.io");
Hope this helps!

Related

How to structure external API calls for Node.js, express, ejs routing?

//API Call one
function receiveLocation(){
axios({
"method":"GET",
"url":"https://ip-geo-location.p.rapidapi.com/ip/check",
...
})
.then((response)=>{
return response.data.country.name;
})
.catch((error)=>{
console.log(error)
})
}
//API Call two
//API Call three
console.log(receiveLocation());
app.get("/", function(req,res){
var location = receiveLocation();//
//Then render all the data from my API calls such as location, currency,
//etc. in my landing page. Also use that data on the backend.
res.render("landing",{location:location});
});
I am currently trying to make a website that uses multiple API calls to get information such as location, currency, and other things of a user who loads the website.
I am attempting to receive all the information from the different API calls as I go through the get request route that allows a user to see the landing page. Firstly, I am not even sure if this is allowed. If it is allowed/standard practice, what am I doing wrong in this example. I am attempting to call a function that in the get route to the root page that returns the country of a visiting user. But after doing some console.log() debugging I see that that information is never being received in the get route. Last note: I want to use the info from the API calls both to change what the user sees, and for some calculations that would need to be run on the backend.
If this is not allowed/not standard practice, may someone explain what I should do instead/point in the right direction as to what I should learn to get a better understanding of what I am trying to do
What you are trying at the moment to do is not standard practice, you should lookup MVC for Express,there you will learn how to structure your backend code so that the GET Routes will be used as Views ( these will be your server getting some public files like html,css,javascript that will be passed some information from the Controllers,this is done by using some server-side renders like EJS).
I recommend taking this Udemy course https://www.udemy.com/course/nodejs-the-complete-guide/ for a full understanding,but if you don't have the time,lookup node.js mvc with express, there is plently information about this.

At what point are request and response objects populated in express app

I’m always coding backend api’s and I don’t really get how express does its bidding with my code. I know what the request and response objects offer, I just don’t understand how they come to be.
This simplified code for instance:
exports.getBlurts = function() {
return function(req, res) {
// build query…
qry.exec(function(err, results) {
res.json(results);
}
});
}
}
Then I’d call in one of my routes:
app.get('/getblurts/, middleware.requireUser, routes.api.blurtapi.getBlurts());
I get that the function is called upon the route request. It’s very abstract to me though and I don’t understand the when, where, or how as it pertains to the req\res params being injected.
For instance. I use a CMS that modifies the request object by adding a user property, which is then available globally on all requests made whether ajax or otherwise, making it easy at all times to determine if a user is logged in.
Are the req and res objects just pre-cooked by express but allow freedom for them to be modified to your needs? When are they actually 'built'
At its heart express is actually using node's default http-module and passing the express-application as a callback to the http.createServer-function. The request and response objects are populated at that point, i.e. from node itself for every incoming connection. See the nodeJS documentation for more details regarding node's http-module and what req/res are.
You might want to check out express' source code which shows how the express application is passed as a callback to http.createServer.
https://github.com/expressjs/express/blob/master/lib/request.js and https://github.com/expressjs/express/blob/master/lib/response.js show how node's request/response are extended by express specific functions.

render page while making http request express

Hi i'm an express noob.... I have an api look page, that's all working but what i'd like to have is once a user hits the route i'll display a loading page, fire off the api http request then once it's successful redirect/render the results page. As i understand it you can't use res.render twice on the same route? Maybe our chum next(); can help here?
This is what i have so far:
router.get('/lookup/post/:url', function(req, res){
// Render the loading page...?
res.render('loading');
Lookup.post(req.params.url, function(err, result){
if(err){
}else{
// ...Then once the api lookup comes back ok redirect or render the results page?
res.render('results', {
posts : result.store.postData.posts[0],
votes : result.store.voteData
});
}
});
});
The solution to your problem is to do part in the UI and part on the server. You can do it with an Ajax call or by using Socket.IO, which will create a socket connection to the server.
I would argue that the later is the most convenient solution, because you can talk to the back-end and the front-end by emitting and listening to messages. The cool part of Socket.IO is that if the browser doesn't support sockets, it will default to an Ajax call.
The official website of Socket.IO is: http://socket.io. You can also check my bPhone project where I use Socket.IO in the simplest way possible. Plus my code have a lot of comments that should make everything super clear.
I hope this will put you on the right path :)

React Node API Request Design Pattern

I need to make an API request to an external API using an API Key. I know how to make this API request in React by writing a onSubmit function. But since I have an API key that I want to keep a secret I am going to write a simple Node app to house env variables.
Besides messing around in node this is my first production experience with Node and I am wondering if my thought process is correct and if not, the better way to do this.
Most of this question will be pseudo code since I haven't started with the Node portion yet.
The idea is that from within the React component it would call the Node app who in turn would call the external API.
React -> Node -> External API
So the React component would be something like so:
handleSubmit: function() {
var data = this.refs.testData.getDomNode().value;
$.ajax({
url: '/my-node-endpoint',
dataType: 'json',
type: 'POST',
data: { test: data },
success: function(data) {
// Whatever success call I want to make
}.bind(this)
})
}
And then in my Node app it would like something like this:
app.post('/my-node-endpoint', function(req, res) {
// Store the values we are posting as JSON
// Start the post request
// On End tell the React component everything is ok
// Prosper
});
As always, thanks for any help that is offered.
Your thought process looks right to me.
If the API you are calling is from a different domain, you will have to build a wrapper on your node server like you did here. Unless the external API supports cross-origin requests with no domain restrictions (such as MapBox web services), you will have to do this.
Several improvements to your code:
As far as I know, you can use React.findDOMNode(this.refs.testData).value instead of this.refs.testData.getDomNode().value. getDomNode() is deprecated in v0.13.
For all the AJAX calls, you can use the Store concept in Flux. The store keeps the states of the data, including updating data through AJAX request. In your React UI code, you just need to call the methods of the store, which makes your UI code clean. I usually create a store class myself without using Flux.

Add data to couchdb with jsonp response

Is there a way add data to a couchdb that runs on another domain and get back an response whether the operation was successfully or not? I know couchdb supports jsonp callback but can I add data with this approach?
No, you cannot currently do this. CouchDB's REST API requires a POST or PUT request in order to insert data, but JSONP only supports GET requests. So you can retrieve data from CouchDB across domains, but updates/inserts/deletes won't work.
You can use client-side javascript to make a form to do the POST, direct the output to an iframe, and use cross-window iframe messaging to get the result.
Of course, someone has already made a nice javascript library to do this. Get the code here:
https://github.com/benvinegar/couchdb-xd
Follow the instructions to push it as an additional database on your couchdb server. Then, on any site, include one not in the 'your-couch-server' domain, you can do the following (just try it in the javascript console):
jQuery.getScript(
"http://YOUR-COUCH-SERVER/couchdb-xd/_design/couchdb-xd/couchdb.js",
function() {
Couch.init(
function() {
var s = new Couch.Server('http://YOUR-COUCH-SERVER/');
var d = new Couch.Database(s,'YOURDB');
d.put(
"stackoverflow-test 1",
{ foo: 111, bar: 222 },
function(resp) {
console.log(resp);
}
);
}
)
}
);
The above presumes you have jquery is already loaded on the page. If not, you'll need to add it however you're currently interacting with the other page.
The library only works on modern browsers with window.postMessage() support, though a small patch may eventually allow older browsers to use it via src/hash communication.

Resources