Let me preface by saying that I have spent a considerable amount of time trying to figure out the solution to this problem but I have not discovered something that works. I am using node and want to share a variable between my app.js server file and a client side javascript file (demo.js).
I run node app.js to launch the server and demo.js runs in the client. I have tried using module.exports and export but when I try importing in the demo.js file or referring to the module.exports var I get errors. Maybe I'm approaching this is in the wrong way.
For example, I am trying to use the node wikipedia package to scrape data. I have the following in my app.js file:
var wikipedia = require('node-wikipedia');
wikipedia.page.data('Clifford_Brown', { content: true }, function(response) {
console.log(response);
export const response = response;
module.exports.data = response
});
In my demo.js file I have tried importing this response var and using the module.exports var but I have been unsuccessful.
Anyone have any solutions to this issue or different approaches I should take?
Browser javascript files run in the browser. node.js javascript files run on the server. You cannot directly export things from one to the other. They are on completely different computers in different locations.
It is very important for developers to understand the notion that server-side code runs on the server and client-side code runs on the browser. The two cannot directly call each other or reach the other's variables. Imagine your server is in a data center in Seattle and the browser is running on a computer in Venice.
See How to access session variables in the browser for your various choices described for a previous answer.
In a nutshell, you can have the server insert a javascript variable into the generated web page so that when the javascript runs in the web page on the browser, it can then access that variable in its own page. Or, you can create an Ajax call so the client can request data directly from the server. Or you can have the server put some data in a cookie which the Javascript in the browser can then access.
If the data is easily known by the server at the time the page is generated and you are using some sort of page template system, then it is very easy to just add a <script> tag to the generated page that defines one or more Javascript variables that contain the desired information. Then, the client-side Javascript can just refer to those variables to have access to the data.
To pass data in http there is a request message and response message and the data needs to be inside that message.
In the request you can either pass variables in the request URL
http://host_name/path?key=value
Or inside the request body or headers.
In the response you pass back variables in the response header or response body
First Example:
One way of processing a URL request from the browser explicitly while passing variables is to set up your server to render a html page with those variables embedded.
If you use a templating engine like jade, you can consume the sent variables directly into the template using res.render({ key: 'value' }) rather than using a promise based api call which would run when the user performs some action on the client.
For instance.
// SERVER setup rendering engine
app.get('/', function(req, res) {
res.render( 'index', { key: 'value' })
}
Which will render index.html to the client with the key-value pair passed to the template file used to serve up the index.html (for example jade or ejs).
Second Example:
Using axios you can set up an action to call a server api (you can also pass variables in the URL, headers or body). Using the promise pattern you can then use these variables after the server api has responded.
// CLIENT setup axios
axios.get(URL + '/getkeyvalue')
.then(function(response) {
const value = response.data.key
})
On you server using express you (this is where you would get the optional request variables mentioned above) send back response variables in the body like this.
// SERVER setup express
app.get('/getkeyvalue', function(req, res) {
res.send({ key: 'value' })
}
Note that these are simple examples.
They are too completely different systems. The best way to accomplish what you're trying to do is the create a variable in your html on the server side by stringifying your data
<script> var my_data = <%= JSON.stringify(data) %> </script>
Thats an example using ejs, a common templating language in expressjs
Related
I have a large program written in C# using Visual Studio that is running as a standalone executable complete with an extensive GUI interface. We want to add a web interface to the program in order to get most, if not all of the functionality available.
The first step I took was to add a web server to the C# executable and I linked in some web pages (html and css) and I was able to present some data as a test case and this worked.
We decided that it would be better to treat the standalone executable like a database where we would prefer to send requests to the get data and post data to store data.
I'm using Node.js framework and Express as my webserver. I have a number of web pages designed and I'm using javascript and some embedded javascript to render data on some of the web pages.
The issue I'm having is how do I process a selected route on one of my webpages to force the the .js file that is processing the route to go out and access the executable program that is running on the same computer at this moment to both get and set data in the executable program and then present it back on the web page.
HTML code (test_data_02.ejs)
<input class="LSNButton1" type="button" style="color:black; background:green;" value="Test Button B" onclick="window.location.href = '/get_test_02b'" />
Javascript (Express) (main.js)
const { json } = require("express");
const express = require("express"),
app = express(),
fs = require("fs"),
homeController = require("./controllers/homeController"),
errorController = require("./controllers/errorController"),
data_entry_03 = require("./public/js/data_entry_03"),
layouts = require("express-ejs-layouts");
app.set("view engine", "ejs");
app.use(layouts);
app.use(express.static("public")); //tell the application to use the corresponding public folder to serve static files
app.use(express.urlencoded({ //assists in reading body contents, parse incoming requests in url format
extended: false
}));
app.use(express.json()); //assists in reading body contents, parse incoming requests in json format
app.set("port", process.env.PORT || 3000);
console.log("MAIN MAIN MAIN"); //this only happens once on start up - this file is only executed on startup, everything is configured on startup
//MIDDLEWARE CODE
app.use((req, res, next) => { //this is a generic middleware function that will be called before any route is processed
homeController.middleWareFunction(req, res); //process this function before moving on to next route that was selected
//console.log(req.query); //from the browser: http://localhost:3000?cart3&jack=5, this results in a request to /?cart=3&jack=5, how do i make a post for this
console.log(`request made to: ${req.url}`);
console.log("");
next();
})
//app.use("/general_data", homeController.readDataFile); //run this function for every request made to the path beginning with this route - it does not imply it is a middleware that will run on this route before the get
//GET ROUTE CODE
app.get("/", homeController.showHome); //a specific route has been identified and the callback function assoicated with the particular route is specified
app.get("/specific_data", homeController.showSpecificData);
app.get("/general_data", homeController.showGeneralData);
app.get("/get_test_02b", homeController.getTest02B);
More Javascript (homeController.js)
I was hoping somewhere in 'getTest02B' or in 'getTestData02B' I would be able to send or request data from the executable program somehow. Maybe using a URL. I would obviously need to add code to my executable to process the requests to either send or receive data. I would like to be able to process the data as JSON, XML, or text data in both directions. I just can't see the mechanism to perform this operation.
exports.getTest02B = (req, res) => {
console.log("in exports.getTest02B");
displayInformation(req.url, "URL");
displayInformation(req.method, "METHOD");
displayEmptyLine();
getTestData02B(function (err, data) {
if (err)
return res.send(err);
res.send(data);
});
};
One comment on this issue was that I needed to make a http request to the C#
program. I understand that Node.js allows several connections per server to
make HTTP requests.
I'm using the code below and it is not working.
const req = http.request('http://192.168.1.222/ListAlarms.html', (res) => {
console.log('STATUS:${res.statusCode}');
});
req.end;
When I run this code there appears to be no response from the C# program.
From the editing environment where I wrote the code above, if I select the URL and press CONTROL and CLICK I end up launching another browser with the data being displayed because the C# program is currently running. This implies to me that at least the URL in the http.request statement is good. Just to confirm the URL was working I ran the C# program in the debugger and I was able to break on receiving the URL. The URL is good but I just can't seem to access the C# program from the Node.js environment using the same URL. Obviously I'm doing something wrong.
I assume that once I'm able to generate a http.request from the Node.js environment I would be able to control the flow of data to and from the C# program.
Any input would be appreciated.
Currently I am making a page that display's data gathered from an API. Most of the data is updated on the server side every 4 hours but some of it is updated whenever a client requests the index route. As a result, there is a delay in the index file being sent since the data needs to be updated. I want to gather the updated data after the page has been requested and sent so there is no delay. My first idea was to make the request on the client side which will handle updating the display after the data is gathered but from my knowledge, I don't know how to do that without giving them the API key. Should I approach the problem this way or is there a better way to do it? I'm using Express for the back-end, Axios is used to make the get requests, and EJS is the template engine.
Here is the code:
// This is called before the data is send in a for loop
data.gameData[i].player_count = await SteamModule.liveGetCurrentPlayers(data.gameData[i].appid);
res.render('index', {data: data});
// This is the function that is called
liveGetCurrentPlayers: async (id) => {
const res = await axios.get(`${base}/ISteamUserStats/GetNumberOfCurrentPlayers/v1/?key=${key}&appid=${id}`, {timeout: 1000}).catch(err => {
Logger.error("Error getting updated user data per request");
return 'Error';
});
if(res.data) {
return res.data.response.player_count;
} else {
return 'Error';
}
}
Here's a bit of a drawing to explain what I've said in comments.....
(the code you showed should constantly update - without other info I can't help with whatever the other issue was, but this is the overall idea....)
Where:
Client requests data from you (your server)
Your server sends html and css to show a 'frame' of the page (no data, just something for them to see and feel like something is happening...)
Your server requests data from the API server (all the various "20-ish" things you said you wanted to serve....)
As the data is updated (or you may have it already), you send the data to the client, updating their 'frame' page with current data.
You can maintain your keys on the server side and add restriction that those API's can only be accessed by your client side URL. So you would access the API and it will maintain your session and handle the authorized KEY part as well.
Anything and everything on the client side is accessible if it running in your browser.
You can add security measures on the server but not on client side for protecting your key.
I'm building my first Express app, which needs to interact with an API, using an API key that ideally remains secure.
So I wanted to follow a basic pattern of keeping the key (and any future environment variables), in a .gitignored .env file in the root directory.
To not reinvent the wheel, I used this package, and set my env variables like so, in my app.coffee file (the root file of the application):
env = require('node-env-file')
env __dirname + '/.env'
console.log process.env.MY_API_KEY
That console.log prints out the right key to the server logs. The problem arises later:
If I try to access that same variable in one of the JS files loaded later on by my app, process.env is an empty object, so the API key is undefined. This doesn't appear to be a problem with the above package, because if I define the variable in the CL (API_KEY=whatever npm start), the behavior is the same -- it console logs correctly from app.coffee but is unavailable later.
Some information on how the files in which the key is unavailable are being loaded:
The app is running React, which I write to a few .jsx files in public/javascripts/src, and which are compiled by gulp into public/javascripts/build/*.js.
I'm trying to access the key in a .js file in public/javascripts/ which is required by one of the .jsx files.
In that required .js file, process.env returns an empty object. When I try to access process.env in the .jsx files, I'm actually told that process itself is undefined.
Any ideas what's going on here? I'm new to Express/React, and unclear where this process object, which I thought was global and defined on npm start is defined, and what's happening to all the env info in it.
Thanks! Please let me know if any other information would be helpful, orif anyone has any suggestions for how better to handle private env info in my situation.
EDIT:
I tried the suggestions below, and created a separate endpoint internally, which hits the external API and then returns a response. I've strung things up correctly, so that this responds correctly:
router.get '/images', (req, res, next) ->
res.json({ some: 'json' });
but this (which uses a separate class to make a request to an external API), throws an error:
router.get '/images', (req, res, next) ->
new Images('nature').fetch (images) ->
res.json({ some: 'json' })
Essentially, it looks like the asynchrony of the response from the external API (and not even the data itself, which I ignored), is creating a problem. How do I hit this external endpoint and then respond to the internal request with the incoming data?
Back-end vs Front-end
It seems like you are trying to access back-end data from a front-end location, in a wrong way.
The great power of Node.js is having JavaScript in the front and in the back, but it is quite confusing in the beginning to understand on which side each script is executed.
In an Express project, all Javascript files that are sent to the front-end, those that will directly interact with the client's page, are located in public/javascripts/. Generally you will have some AJAX functions in some of those files to exchange data and communicate with the back-end.
These back-end files are located everywhere else : in the root directory, in routes/, and all the other folders you create. Those files are pretty much all connected to your Node instance, and therefore can communicate with each other using global objects like process for example.
Your script in public/javascripts/, that is executed on the client's computer, is trying to directly access a variable located on the server running your Node instance : that's why your code doesn't work. If you wish to access data from the back-end, you must use AJAX calls in the front-end.
Server <---(AJAX only)--- Client
------ ------
app.js public/javascripts/script.js
routes.js
...
That being said, you wanted to keep your API key private, which will not happen if you send it to every client who's on that specific page. What you should do is make the call from the back-end, using the xhr module for example, and then delivering the data to front-end, without the secret API key.
I hope I was clear, Node is quite confusing at first but very soon you will get over these little mistakes !
All .jsx is, is some code, what matters is where the code is being executed. process.env is a variable that is accessible inside the Node.js runtime. When your .jsx code gets transpiled down to .js and served to the browser, the process.env variable will no longer exist. If you're making an API call inside the browser, the API key will be fundamentally available to the client. If you want to secure the key, you have to have your Node.js server expose an API route, which your React app will hit. That Node.js server will then make the call to the external service using the API key. Because that call is being made by the server, process.env will be available, and will remain hidden from the client. You can then forward the result of the API call back to the user.
I'm developing extension to an existing app using expressjs. By requirements I need to do server side rendering. Our API server is running on JAVA.
In my express app, I'm going to have to render page while making serverside calls to JAVA server. For example, I'm going to call getCategories, getVendors, getReviews etc APIs in order get data that I need to pass to rendering template.
What is a good way to accomplish this? I mean pack all of that into a single object and pass to res.render() method. Could you please provide me with some basic examples? I imagine that I would need to create a module that does all of this and returns some nice object...
All of the examples on the internet show something like res.render('index', { data: 'someData'}), however in this case data is bootstrapped. I would need a data to be compiled with several API calls and then passed to the rendering method.
Thanks!
It really depends on whether you want to call your Java API server side or client side.
If server side, your express route containing res.render() will need to collect all the information it needs from Java before rendering the page. If your API is slow, the page load will be slow. If possible, use something like async.parallel to avoid blocking.
Your code sample is correct. res.render('index', { foo: 'bar'})
This would add the foo variable to the root scope of your view. How you build up the data structure is entirely up to you. I would recommend using async.parallel and/or async.series to collate all of the data. If they are all completely independent, run them in parallel.
// an example of how to run each in parallel and then render the view.
var getCategories = function(done) {
// get something
done();
};
var data = {};
async.parallel([
getCategories, // <-- adds to the data structure
getVendors,
getReviews
], function(err) {
res.render('index', { data: data });
});
Your other option is using express to render the base page and using something like jQuery to hit the Java API and update the relevant page fragments.
I have a to access the config variables defined in the file called
test.js which has --
var aws = require('aws-sdk');
exports.connect = function(){
return aws;
}
Now I need to access it when the OnClick event occurs on the browser. I have this script but the require module does not work.
clientScript.js
var aws = require('../scripts/test.js').connect();
function getValue() {
aws.describe({},function(){...})
}
How can I access this aws variable?
Hopefully I'm not too far off the mark with what you're trying to do here. My understanding (cobbled together between this and your previous question is that you would like something in the browser that upon click will retrieve some status information from an external API, which will then be displayed in the client.
What I would recommend doing (based on the above assumption) is defining your desired function as something to be triggered by an HTTP request to the Express server, which can perform your function and send whatever you'd like from its process back to the client.
In your server define (assuming your Express variable is app)
app.get('/request', someFunction);
In someFunction define what it is you'd like to do, how it relates to the request and response, and what to send back to the client. Express will expect the function to take request and response as arguments, but you don't necessarily need to use them:
someFunction(req,res) {
//do whatever I'd like with aws or anything else
res.send(foo); //where foo is whatever JSON or text or anything else I'd like the client to have to manipulate
}
On the client, you would have a function bound to onclick that would make the request to that /request endpoint. This function could use AJAX or simply render another page entirely.
This sort of organization also leaves display and behavior to the client, while the server deals with retrieving and manipulating data. This layout also resolves any concerns about require() statements on the clientside (which, while possible with things like Browserify, are not necessary and may make the code more confusing).
You can't use require() on the client side, that is only a server side function provided by node.js which runs on the server. If you need config options that are shared between server and client, then you will need to make a few changes to your test.js file so it will work in both. I'm sure there are a number of ways to do this, but the way I prefer is:
Put all your configuration variables inside test.js into an object like:
this.ConfigOptions = {option1:value1, option2:value2};
The client would include the file like this:
<script src="test.js" type="text/javascript"></script>
and can access the config options via the ConfigOptions object
while the server would use require() to include the file and access the config options like this:
var ConfigOptions = require('test.js').ConfigOptions;