I am pretty new to node.js. I am working on an app able to display NFC content on a webpage. I am using nfc-pcsp package (https://github.com/pokusew/nfc-pcsc), I can easily read data on server side. Now I just would like to display the data in the webpage, but I am stuck on the logic. Here is a part of my server code:
// ### launch server for client
var http = require('http');
var html = require('fs').readFileSync(__dirname+'/custom.html');
var server = require('http').createServer(function(req, res){
res.end(html);
});
server.listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:3000/');
//######
//#launch NFC routine ######
const nfc = new NFC(); // const nfc = new NFC(minilogger); // optionally you can pass logger to see internal debug logs
let readers = [];
nfc.on('reader', async reader => {
pretty.info(`device attached`, { reader: reader.name });
// the event is correctly displayed in the console. How to update html here?
readers.push(reader);
nfc.on('error', err => {
pretty.error(`an error occurred`, err);
});
It seems to me that I need a res object to update the html page, but as I do not get any request from client, how do I update the page just based on the callback from NFC module reader? Hope my question is clear.
Thanks,
Matt
I suggest you to use the express API
command with npm CLI : npm install --save express at your root project folder in your terminal
Then, you will be able to create a route in Get, Post, Put or Delete.
Next, In your client side you will be able to call this route by a get, post or whatever with a promise, ajax request whatever you want :)
Just understand that in order to receive or send data to your server, you need an url and with Express, you can create your own url.
https://www.npmjs.com/package/express
Don't hesitate to have a look on this API, and i'm pretty sure you will find the answer to your question on your own :)
Related
I would like to use the output of my nodeJS. This is my code
var fs = require('fs'); //File System
var rutaImagen = 'C:/Users/smontesc/Desktop/imagenes1/'; //Location of images
fs.readdir(rutaImagen, function(err, files) {
if (err) { throw err; }
var imageFile = getNewestFile(files, rutaImagen);
//process imageFile here or pass it to a function...
console.log(imageFile);
});
function getNewestFile(files, path) {
var out = [];
files.forEach(function(file) {
var stats = fs.statSync(path + "/" +file);
if(stats.isFile()) {
out.push({"file":file, "mtime": stats.mtime.getTime()});
}
});
out.sort(function(a,b) {
return b.mtime - a.mtime;
})
return (out.length>0) ? out[0].file : "";
}
And the result is console.log(imageFile), I want to call the result of this in my javascript project, like
<script>
document.write(imageFile)
</script>
All this is to get the newest file created in a directory because I can't do it directly on JS.
Thanks a lot
First, there are several fundamental things about how the client/server relationship of the browser and a web server work that we need to establish. That will then offer a framework for discussing solving your problem.
Images are displayed in a browser, not with document.write(), but by inserting an image tag in your document that points to the URL of a specific image.
For a web page to get some result from the server, it has to either have that result embedded in the web page when the web page was originally fetched from the server or the Javascript in the web page has to request information from the server with an Ajax request. An ajax request is an http request where the Javascript in your web page, forms an http request that is sent to your server, your server receives that request and sends back a response which the Javascript in your web page receives and can then do something with.
To implement something where your web page requests some data from your back-end, you will have to have a web server in your back-end that can response to Ajax requests sent from the web page. You cannot just run a script on your server and magically modify a web page displayed in a browser. Without the type of structure described in the previous points, your web page has no connection at all to the displayed server. The web page can't directly reach your server file system and the server can't directly touch the displayed web page.
There are a number of possible schemes for implementing this type of connection. What I would think would work best would be to define an image URL that, when requested by any browser, it returns an image for the newest image in your particular directory on your server. Then, you would just embed that particular URL in your web page and anytime that image was refreshed or displayed, your server would send it the newest version of that image. Your server probably also needs to make sure that the browser does not cache that URL by setting appropriate cache headers so that it won't mistakenly just display the previously cached version of that image.
The web page could look like this:
<img src='http://mycustomdomain.com/dimages/newest'>
Then, you'd set up a web server at mycustomdomain.com that is publicly accessible (from the open internet - you choose your own domain obviously) that has access to the desired images and you'd create a route on that web server that answers to the /dimages/newest request.
Using Express as your web server framework, this could look like this:
const app = require('express')();
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
const stat = util.promisify(fs.stat);
// middleware to use in some routes that you don't want any caching on
function nocache(req, res, next) {
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate, proxy-revalidate');
res.header('Expires', '-1');
res.header('Pragma', 'no-cache');
next();
}
const rutaImagen = 'C:/Users/smontesc/Desktop/imagenes1/'; //Location of images
// function to find newest image
// returns promise that resolves with the full path of the image
// or rejects with an error
async function getNewestImage(root) {
let files = await readdir(root);
let results = [];
for (f of files) {
const fullPath = root + "/" + f;
const stats = await stat(fullPath);
if (stats.isFile()) {
results.push({file: fullPath, mtime: stats.mtime.getTime()});
}
}
results.sort(function(a,b) {
return b.mtime - a.mtime;
});
return (results.length > 0) ? results[0].file : "";
}
// route for fetching that image
app.get(nocache, '/dimages/newest', function(req, res) {
getNewestImage(rutaImagen).then(img => {
res.sendFile(img, {cacheControl: false});
}).catch(err => {
console.log('getNewestImage() error', err);
res.sendStatus(500);
});
});
// start your web server
app.listen(80);
To be able to use that result in your Javascipt project, we definitely have to create an API which has a particular route that responses the imageFile. Then, in your Javascript project, you can use XMLHttpRequest (XHR) objects or the Fetch API to interact with servers to get the result.
The core idea is we definitely need both server-side and client-side programming to perform that functionality.
So i am following a tutorial to create the XML doc for twilio like as a response,
(https://www.twilio.com/blog/2013/03/introducing-the-twilio-module-for-node-js.html)
but rather than sending it as a response, I want to generate the file on my server to access later.
something like - localhost/files/USERNAME/FILENAME.xml
this is my current code that sends the file as a response.
var resp = new twilio.TwimlResponse();
resp.say({voice:'woman'}, 'Thanks for calling!');
//Render the TwiML document using "toString"
res.writeHead(200, {
'Content-Type':'text/xml'
});
res.end(resp.toString());
You can save the response from Twillio to your localhost by using the fs library built into node. Specifically:
https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback
Once you have your .xml file saving to localhost, you can use an express route with res.sendFile() that sends back the .xml file.
https://expressjs.com/en/api.html#res.sendFile
Twilio developer evangelist here.
As tomato points out, you can use the build in fs library from Node. You could do that like below:
var resp = new twilio.TwimlResponse();
resp.say({voice:'woman'}, 'Thanks for calling!');
//Render the TwiML document using "toString"
res.writeHead(200, {
'Content-Type':'text/xml'
});
// Save the XML to disk, then return the response to Twilio.
fs.writeFile('twilio-response.xml', resp.toString(), function(err) {
res.end(resp.toString());
});
You may want to generate a unique file name for each response so that they don't get written over.
Just in case this helps, Twilio does save the TwiML responses that you send back and you can retrieve these in your call log.
I know how to send an http request to a server using angular js. With the promise returned, I know how to listen for a response and manipulate the ui thereafter. But this approach cannot be used for what I have in mind.
However, what I cannot figure out, is how to send a request to a website.
I have a server localhost:800/receiveData which receives a POST request and then manipulate the UI and DoM on the angularjs site
app.get('/', function(req,res){
res.sendFile(__dirname+'/index.html')
})
app.post('/receiveData', function(req,res){
var data = req.body.data
// assume data is a boolean
if(data){
//show a view in index.html using angular js or anything else
}else {
//show a different view in index.html
}
});
Any help will be greatly appreciated. I have a need for angular js. Having a SPA is imperative. I am completely open to adding additional stacks if neccessary.
EDIT:
As pointed out by MarcoS, manipulation of dom should ideally not happen from the server side. I am combining IPFS with node js and angular js to develop a single page application. The swarm of nodes set up using IPFS has an open line of communication with my server (by design). Based on packets of data sent via the comm line to my server, I need to convey messages to the user via the index.html.
I think your approach is wrong: on server-side, you should NOT manipulate the UI and DOM...
You should just do server activity (update a database, send an email, ..., return a static page).
Then you can output a result (JSON/XML/... format) for your client-side calling script to read.
Following OP edit, what I do understand is he wants server push to the client.
To get serve side pushes, you should poll on the client.
In a controller:
function getServerState(changeState) {
return $http.get("/receiveData").then(function(res) {
changeState(res.data); // notify the watcher
}).catch(function(e) {
/* handle errors here */
}).then(function() {
return getServerState(changeState); // poll again when done call
});
}
Consuming it this way:
getServerState(function(status) {
$scope.foo = status; // changes to `foo` $scope variable will reflect instantly on the client
});
And, server side:
app.post('/receiveData', function(req, res) {
var data = req.body.data; // assume data is a boolean
res.end(JSON.stringify(data);
});
I'm running a small Angular application with a Node/Express backend.
In one of my Angular factories (i.e. on the client side) I make a $http request to Github to return user info. However, a Github-generated key (which is meant to be kept secret) is required to do this.
I know I can't use process.env.XYZ on the client side. I'm wondering how I could keep this api key a secret? Do I have to make the request on the back end instead? If so, how do I transfer the returned Github data to the front end?
Sorry if this seems simplistic but I am a relative novice, so any clear responses with code examples would be much appreciated. Thank you
Unfortunately you have to proxy the request on your backend to keep the key secret. (I am assuming that you need some user data that is unavailable via an unauthenticated request like https://api.github.com/users/rsp?callback=foo because otherwise you wouldn't need to use API keys in the first place - but you didn't say specifically what you need to do so it is just my guess).
What you can do is something like this: In your backend you can add a new route for your frontend just for getting the info. It can do whatever you need - using or not any secret API keys, verify the request, process the response before returning to your client etc.
Example:
var app = require('express')();
app.get('/github-user/:user', function (req, res) {
getUser(req.params.user, function (err, data) {
if (err) res.json({error: "Some error"});
else res.json(data);
});
});
function getUser(user, callback) {
// a stub function that should do something more
if (!user) callback("Error");
else callback(null, {user:user, name:"The user "+user});
}
app.listen(3000, function () {
console.log('Listening on port 3000');
});
In this example you can get the user info at:
http://localhost:3000/github-user/abc
The function getUser should make an actual request to GitHub and before you call it you can change if that is really your frontend that is making the request e.g. by cheching the "Referer" header or other things, validate the input etc.
Now, if you only need a public info then you may be able to use a public JSON-P API like this - an example using jQuery to make things simple:
var user = prompt("User name:");
var req = $.getJSON('https://api.github.com/users/'+user);
req.then(function (data) {
console.log(data);
});
See DEMO
I'm having an angular app(angular-seed app) which should call a function in nodejs(web-server.js).
The function in nodejs is just calls a batch file.
If I understood this correctly you want a click on the client-side (angular app) to call a batch file on the server side. You can do this in several ways depending on your requirements, but basically you want the client-side to send a http-request to the server (either with ajax call or form submit) and process this on the server that will call the batch file.
Client-side
On the client-side you need to have a button that uses the angular ng-click directive:
<button ng-click="batchfile()">Click me!</button>
In your angular controller you'll need to use the $http service to make a HTTP GET request to your server on some particular url. What that url is depends how you've set up your express app. Something like this:
function MyCtrl($scope, $http) {
// $http is injected by angular's IOC implementation
// other functions and controller stuff is here...
// this is called when button is clicked
$scope.batchfile = function() {
$http.get('/performbatch').success(function() {
// url was called successfully, do something
// maybe indicate in the UI that the batch file is
// executed...
});
}
}
You can validate that this HTTP GET request is made by using e.g. your browser's developer tools such as Google Chrome's network tab or a http packet sniffer such as fiddler.
Server-side
EDIT: I incorrectly assumed that angular-seed was using expressjs, which it doesn't. See basti1302's answer on how to set it up server-side "vanilla style" node.js. If you're using express you can continue below.
On the server side you need to set up the url in your express app that will perform the batch file call. Since we let the client-side above make a simple HTTP GET request to /performbatch we'll set it up that way:
app.get('/performbatch', function(req, res){
// is called when /performbatch is requested from any client
// ... call the function that executes the batch file from your node app
});
Calling the batch file is done in some ways but you can read the stackoverflow answer here for a solution:
node.js shell command execution
Hope this helps
The OP didn't mention express so I'll provide an alternative for the server side (Node.js part) without using any additional frameworks (which would require installing it via npm). This solution uses just node core:
web-server.js:
'use strict';
var http = require('http')
var spawn = require('child_process').spawn
var url = require('url')
function onRequest(request, response) {
console.log('received request')
var path = url.parse(request.url).pathname
console.log('requested path: ' + path)
if (path === '/performbatch') {
// call your already existing function here or start the batch file like this:
response.statusCode = 200
response.write('Starting batch file...\n')
spawn('whatever.bat')
response.write('Batch file started.')
} else {
response.statusCode = 400
response.write('Could not process your request, sorry.')
}
response.end()
}
http.createServer(onRequest).listen(8888)
Assuming you are on Windows, I would at first use a batch file like this to test it:
whatever.bat:
REM Append a timestamp to out.txt
time /t >> out.txt
For the client side, there is nothing to add to Spoike's solution.