I have an app where I call an API to get headlines and digest that JSON and send the data back to the client via sockets. I have modularized all my code and used different files to stay organized. I have my index.js file, where I merely call the functions to grab to data from the API and newsDataFetcher.js is where I make the request to the API for the data using the request module. My issue is that since the request I am making is asynchronous, the data doesn't get passed to the main server file and therefore the socket doesn't send any data. After the request to the API finishes, I put the data into an array and that array is in the module.exports of my newsDataFetcher.js file, along with the function that makes the actual request. So since the array is in the module.exports, an empty array is passed when I require the newsDataFetcher.js file in the index.js file. So, that brings me to the question: is there anyway to call module.exports "on demand" so that it doesn't pass an empty array and it only passes the array when the data is finished downloading from the API?
index.js :
var newsDataFetcher = require('./newsDataFetcher');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var news = []; //array for news headlines and abstracts
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/views/routes/'));
server.listen(8888);
console.log("Running server on http://localhost:8888 ....");
newsDataFetcher.getNewsData();
news = newsDataFetcher.news;
setInterval(function () {
newsDataFetcher.getNewsData();
news = newsDataFetcher.news;
}, 6000000);
setInterval(function () {
sendAllData()
}, 1000);
function sendAllData() {
io.sockets.emit('news', news);
}
newsDataFetcher.js file:
var request = require('request');
var nyTimesApi = "http://api.nytimes.com/svc/topstories/v1/home.json?api-key=" + nyTimesApiKey_DEV;
var news = []; //array for news headlines and abstracts
function getNewsData() {
request({
url: nyTimesApi,
json: true
}, function (err, res, body) {
news = body.results;
});
}
module.exports = {
getNewsData: getNewsData,
news: news
}
You can do some like this:
// newsDataFetcher.js
module.exports = {
getNewsData: getNewsData,
news: function() {
return news;
}
}
// index.js
io.sockets.emit('news', newsDataFetcher.news());
You can use singleton pattern to achieve this.
class NewsDataFetcher {
constructor() {
this.news = [];
}
getNewsData() {
const self = this;
request({
url: nyTimesApi,
json: true
}, function (err, res, body) {
self.news = body.results;
});
}
}
module.exports = new NewsDataFetcher();
In the main page.
var newsDataFetcher = require('./newsDataFetcher');
newsDataFetcher.getNewsData(); // to fetch new data;
// To get news data
console.log(newsDataFetcher.news); // will always refer to one instance of news since you are using singleton.
Related
I am troubling with nodejs proxy server modified(write) response.
I want to achieve auto login for one site via node proxy server and for that i have to query in database then i can modified response but it seems req ended before req.write and getting Error: write after end
Below is my implementation so far.
var express = require('express');
var proxy = require('http-proxy-middleware');
var options = {
target: 'http://example.com/', // target host
changeOrigin: true,
onProxyReq: function onProxyReq(proxyReq, req, res) {
var _write = res.write;
var body = "";
proxyReq.on('data', function(data) {
data = data.toString('utf-8');
body += data;
});
res.write = function (data) {
try{
//I have database query here instead of setTimeout
setTimeout(function(){
/* Modified response here and write */
_write.call(res, data); //can't write because req already end
},3000);
} catch (err) {
console.log('err',err);
}
}
}
}
// create the proxy (without context)
var exampleProxy = proxy(options);
// mount `exampleProxy` in web server
var app = express();
app.use('/', exampleProxy);
app.listen(8080);
Can anyone guide me how to achieve this ?
I'll preface this by saying I am new to nodejs in general. Coming from the world of C#, it's a completely different way of thinking for me.
I've gone through a few courses and I'm setting up a little website as sort of a test for myself. And I'm failing!
I'm using socket.io with node, and I'm trying to broadcast a message with the emitter once in a while. I don't care about specific socket points right now (although I will in the future), so the emitter for this should go out to everyone.
I am having trouble accessing the io object from other modules.
I'll post my server.js file, as well as app/index.js, socket/index.js, helpers/index.js and api/index.js. I hope that posting these will show how it's supposed to work.
Ideally, I'd like to keep all socket-related items in the socket module, for consistency. Right now, I'm trying to get a method to run in the helpers module, but ideally the socket module would be better.
Anyway, server.js:
'use strict';
const express = require('express');
const app = express();
const cryptometers = require('./app');
const api = require('./app/api');
const fs = require('fs');
const sources = require('./app/api/sources.json');
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use(cryptometers.session);
app.use('/', cryptometers.router);
cryptometers.ioServer(app).listen(app.get('port'), () =>{
console.log('app listening on port ' + app.get('port'));
api.getData(sources[0].source, sources[0].url, app);
setInterval(function(){api.getData(sources[0].source, sources[0].url, app)}, 60000);
});
Standard fare here. I just added a data retriever that calls to an api once every minute, which updates the database.
app/index.js:
'use strict';
const config = require('./config');
// create an IO server instance
let ioServer = app => {
app.locals.globalMarketCap = [];
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.set('transports', ['websocket']);
io.use((socket, next) => {
require('./session')(socket.request, {}, next);
});
require('./socket')(io,app);
return server;
}
// turns router into a module
module.exports = {
router: require('./routes')(),
session: require('./session'),
ioServer,
}
Here I'm initializing socket.io, binding it to the app. It's also where I initialize a local storage array of data. (Is this a good spot to do this??)
socket/index.js:
'use strict';
const h = require('../helpers');
module.exports = (io, app) => {
io.of('/').on('connection', socket =>{
console.log('socket.io connected to client');
if(app.locals.globalMarketCap){
socket.emit('globalMarketCap', JSON.stringify(app.locals.globalMarketCap));
}
})
}
Here I'm responding to connection events, and pushing out the array of data that I defined in the last file above. Again, ideally I'd like all socket type stuff to stay in here.
helpers/index.js:
'use strict';
const router = require('express').Router();
const db = require('../db');
// iterate through the routes object and mount the routes
let _registerRoutes = (routes, method) => {
for(let key in routes){
if(typeof routes[key] === 'object' && routes[key] !== null && !(routes[key] instanceof Array)){
_registerRoutes(routes[key], key);
} else {
// Register the routes
if(method === 'get'){
router.get(key, routes[key]);
} else if(method === 'post'){
router.post(key, routes[key]);
} else {
router.use(routes[key]);
}
}
}
}
let route = routes => {
_registerRoutes(routes);
return router;
}
let updateGlobalMarketCap = (app) =>{
//app.io.socket.emit('globalMarketCap', JSON.stringify(app.locals.globalMarketCap))
}
module.exports = {
route,
updateGlobalMarketCap
}
The commented out line for updateGlobalMarketCap is where my pain is. Trying to get access to the io object there.
api/index.js
'use strict';
const axios = require("axios");
const db = require("../db");
const h = require("../helpers");
let getData = (source, url, app, cryptoMeters) => {
axios
.get(url)
.then(response => {
//console.log(response.data);
response.data["source"] = source;
var data = new db.globalMarketCapModel(response.data);
app.locals.globalMarketCap = response.data;
var query = { source: source};
db.globalMarketCapModel.findOne({
"source":source
}, 'source old_total_market_cap_usd total_market_cap_usd', function(err, market) {
if (market) {
if(market.old_total_market_cap_usd != response.data.total_market_cap_usd
&& market.total_market_cap_usd != response.data.total_market_cap_usd){
response.data["old_total_market_cap_usd"] = market.total_market_cap_usd;
h.updateGlobalMarketCap(app);
}
db.globalMarketCapModel.update(query, response.data, function (err) {
if (err) {
console.log("uhoh")
} else {
return true;
}
});
} else {
data.save(function (err) {
if (err) {
console.log("uhoh")
} else {
return true;
}
})
}
})
return true;
})
.catch(error => {
console.log(error);
return false;
});
}
module.exports = {
getData
}
The getData function here is where a call to the update emitter would take place.
I've considered using standard node event emitters as a solution to my problem, but that might be gumming up the works and there's a simpler answer.
Anyway, thanks for reading, and I'm interested in any commentary on what i've written so far. pitfalls, mistakes, etc. Learning here! :)
There are many different ways to organize your code to accomplish sharing of the io object. Here's one such scheme. You break out your socket.io initialization code into its own module. You give that module two main features:
A constructor function (that you pass the server to) that allows socket.io to initialize itself on your server.
A method to get the io instance after it's been initialized.
This will allow any other code in your project that wants to get access to the io object to do something like this:
const io = require('./io.js').getIO();
Here's how that io module could be structured:
// io.js
// singleton instance of socket.io that is stored here after the
// constructor function is called
let ioInstance;
modules.exports = function(server) {
const io = require('socket.io')(server);
io.set('transports', ['websocket']);
io.use((socket, next) => {
require('./session')(socket.request, {}, next);
});
// save in higher scope so it can be obtained later
ioInstance = io;
return io;
}
// this getIO method is designed for subsequent
// sharing of the io instance with other modules once the module has been initialized
// other modules can do: let io = require("./io.js").getIO();
module.exports.getIO = function() {
if (!ioInstance) {
throw new Error("Must call module constructor function before you can get the IO instance");
}
return ioInstance;
}
And, this module would be initialized like this:
const io = require('./io.js')(server);
Where you pass it your web server so it can hook to that. It has to be initialized like this before anyone can use .getIO() on it. The storage in the module of the ioInstance makes use of the module caching. The module initialization code is only run once. After that, the same exports are just returned each time which have access to the saved ioInstance inside the module.
I'm struggling with what feels like the final step in passing some data from a model file back into a controller using Node Request.
I've successfully set up a callback from my model file which uses request to load JSON from an external source.
My controller can access this, but I think I still need some kind of nested second callback in the final step as I want the variable pageJSON to contain the JSON object and can't quite figure out how.
Think I've hit a bit of a brick wall with this and some fresh eyes on the problem would be appreciated! It feels like I'm missing something really simple at this point (I hope!)
My model file:
module.exports = function (config, callback) {
const request = require('request');
const options = {
'url' : config.urls.page,
'json' : true,
'auth': {
'user': config.auth.username,
'pass': config.auth.password
}
};
request(options, (error, response, body) => {
if (error) {
console.log(error);
}
callback(body);
});
}
My controller file:
const express = require('express');
const router = express.Router();
const app = express();
const config = require('../config');
const page = require('../models/page');
let pageJSON = page(config, (json) => {
console.log(json); // This shows the JSON structure in console
return json;
});
console.log(pageJSON); // Undefined
// Manipulate JSON and pass request view accordingly using Express
You will have to deal with json manipulation within your controller callback (or call another callback from it):
let pageJSON = page(config, (json) => {
console.log(json); // This shows the JSON structure in console
processJSON(json);
});
pageJSON is undefined because nothing is returned from your model.
I have the following code for implemetation of nodejs a rest api.
app.js
var connection = require('./database_connector');
connection.initalized(); //guys connection is i want to pass a connection varible to the model
var peson_model = require('./models/person_model')(connection); //this not working
var app = express();
app.use(bodyparser.urlencoded({extended: true}));
app.use(bodyparser.json());
app.get('/persons/', function(req, res) {
person_model.get(res); // retrive get results
});
// .............express port and listen
person_model.js is a model class that is supposed to retrieve based on the http verb. For example person.get retrieves the following and currently has a single method as follow.
function Person(connection) {
this.get = function (res) {
connection.acquire(function(err, con) {
con.query('select * from person limit 3', function(err, result) {
con.release();
console.log("get called");
res.send(result);
});
});
};
}
// ** I want to pass a connection variable to the model
module.exports = new Person(connection);
In the code above, var peson_model = require('./models/person_model')(connection); is not working.
How do I pass the connection variable and export the module?
If you return a function from your export, you can pass your parameter.
module.exports = function(connection) {
return new Person(connection);
};
You will need to set this.connection and use that inside your function though.
I am using Node.js, Express, MongoDB, and Mongoose. I have a function that fetches the largest id number of a document in my MongoDB database and returns it to the program. I have begun modularizing my code, and have migrated that function to another module. I have successfully accessed the function in my main module, but it involves an asynchronous database query. As the function returns a value, I want to assign it to a variable. Unfortunately, When the returned value is assigned to the variable, the variable is actually set to undefined. I was thinking about using event emitters to signal that the query is finished, but that presents two issues as well:
1) I don't think you can do anything in a program AFTER a return statement, which would be what is required.
2) Event Emitters between modules seem very finicky.
Please help me get the variable to be assigned to the correct value. Code for both the main function and the module is below:
(main file) app.js:
//requires and start up app
var express = require('express');
var mongoose = require('mongoose')
, dbURI = 'localhost/test';
var app = express();
var postmodel = require('./models/post').postmodel;
//configures app for general stuff needed such as bodyParser and static file directory
app.configure(function () {
app.use(express.bodyParser());
app.use(express.static(__dirname + '/static'));
});
//configures app for production, connects to mongoLab databse rather than localhost
app.configure('production', function () {
dbURI = 'mongodb://brad.ross.35:lockirlornie#ds037387.mongolab.com:37387/heroku_app6901832';
});
//tries to connect to database.
mongoose.connect(dbURI);
//once connection to database is open, then rest of app runs
mongoose.connection.on('open', function () {
var PostModel = new postmodel();
var Post = PostModel.setupPostSchema();
var largest_id = PostModel.findLargestID(Post);
(module) post.js:
var mongoose = require('mongoose');
module.exports.postmodel = function () {
this.setupPostSchema = function () {
var postSchema = new mongoose.Schema({
title: String,
body: String,
id: Number,
date_created: String
});
var Post = mongoose.model('Post', postSchema);
return Post;
};
this.findLargestID = function (Post) {
Post.find(function (err, posts) {
if (err) {
console.log("error finding largest ID!");
} else {
var largest_id = 0;
for (var post in posts) {
if (posts[post].id >= largest_id) largest_id = posts[post].id;
}
console.log(largest_id);
return largest_id;
}
});
};
};
You need to have findLargestID accept a callback parameter that it will call once largest_id is available:
this.findLargestID = function (Post, callback) {
Post.find(function (err, posts) {
if (err) {
console.log("error finding largest ID!");
callback(err);
} else {
var largest_id = 0;
for (var post in posts) {
if (posts[post].id >= largest_id) largest_id = posts[post].id;
}
console.log(largest_id);
callback(null, largest_id);
}
});
};