Simple Node.JS application to read / write to a local file - javascript

I have a file with 10 variables stored in it, locally on my system. I want this to be available to someone else when they connect to my system. This will be used within my LAN only and not over the internet so there is no need to worry about security. I want to create a simple client-server application that uses HTTP request/response to communicate.
I can work with HTML and Javascript but I have limited knowledge of Node JS. I have created a HTML page with 10 blank labels and a "show" button. When I click on the button, I want the file to be read and each of the 10 variables to be displayed in the labels.
Here is a sample with two variables and a button.
**index.html**
<html>
<body>
<form method="post" action="/">
<fieldset>
<legend><h2>Parameters:</h2></legend>
System ID : <textarea name="my_id" id="my_id" size="2"></textarea><br >
System IP : <textarea name="my_ip" id="my_ip" size="15"></textarea><br>
<input id="show" type="submit" name="show" value="SHOW"><br>
</fieldset>
</form>
</body>
</html>
How do I go about this? Is there any easier way besides Node JS ?
If this works I would also like to over-write the variables in the same file with the user's inputs (using a different "submit" button).

I would recommend just using the http module along with the fs module for this but since you are still learning nodejs, I would recommend expressjs and flat-file-database
So when you are creating routes for your small webapp, it would probably look like this :
// Serves html file
app.get('/', function(req, res) {
res.sendFile('/index.html');
});
// Stores data from the form
app.post('/form', function(req, res) {
var data = req.body;
db.put('data', data); // Saves data to the flat file db
res.status(201);
});
// Gets data from the db
app.get('/form', function(req, res) {
var data = db.get('data');
res.status(200).json(data);
});
You would need to 'post' the data at /form in your client side code.
To get the data, simply issue a get request at /form
Also keep in mind that for express to parse form data, you need the body-parser middleware.
Oh and note that I have no idea whether flat-file db is asyncronous or not so you will probably need to check that yourself.

Related

Is there a way to take user input from a form and append it to code I already have?

I have html and css code for a basic quiz template. I want to give the user the ability to make their own custom quiz.
Example: I have created my own math quizzes, science quizzes, etc, that the user can take. I am looking for the ability that Users can make their own personal quiz.
You don't append users input to your code. You should have your quiz as a data and let the user update the data by adding their quiz.
The structure of a form looks like this:
<form method = 'post' action='./handleSubmission/'>
<label>Question 1: </label>
<input type='text' class='question' name='question1'>
<label>Answer 1: </label>
<input type='text' class='answer' name='answer2'>
<label>Question 2: </label>
<input type='text' class='question' name='question2'>
<label>Answer 2: </label>
<input type='text' class='answer' name='answer2'>
<button>Submit</button>
</form>
(You can find all the different input types here. You might want another type for multiple choice questions.
When the user clicks on submit, the default behaviour is that the content of the form will be sent as an http request to the action url. if you set post as method, the method will be POST. If you set get as method, the method will be GET.
Now, in order to do something useful with it, there needs to be a server-side script at './handleSubmission/' or whatever url you put in here, that can read the sent data and upload it to some place where you store the data for your quizzes. This can be either a database or a repository containing some files.
I'd go for json files. Because json files can very easily be decoded and used in any web scripting language.
In PHP for example you'd get the content of the form through a special array called $_GET (or $_POST depending on the method).
You'd then have access to 'question1' with $_GET['question1'].
You'd then have to find a way to put that data into a json file.
To use the content of the json files, you can either use a backend script or a frontend script like javascript.
Are you already using a scripting language for the backend such as PHP or Python? Or do you focus on frontend?
If you want to focus on javascript and frontend, this is the alternative:
<form>
//...
<button id='btn-submit'>Submit</button>
</form>
As you can see, i ommited action and method because in this alternative we don't want to send the form to the server. What we'll do is, when the button is clicked, we'll capture the content of the form without refreshing the page, and then send it a Backend-as-a-service like Google Firebase.
const submitButton = document.querySelector('#btn-submit');
submitButton.addEventListener('click', (e) => {
/* important! prevents the default behaviour which is to submit the form */
e.preventDefault();
const data = [];
/* do stuff here to retrieve the data from form like: */
const questionInputs = document.querySelector('.question');
const answerInputs = document.querySelector('.answer');
for(let key in questionInputs){
data[key] = {
question: questionInputs[key].value;
answer: answerInputs[key].value;
}
}
sendToFirebase(data);
});
You'd then have to write the sendToFirebase function.
Firebase requires making an account, starting a project by giving a name etc. Then it gives you the code to put in your app and you can read the documentation about how to upload data to the Realtime Database.
I strongly prefer the first option however. Because i think in this case the Firebase Realtime Database would be a bit cumbersome to use compared to just setting up a small backend script that generates json files.

Combining client-side jQuery and server - side Express

I'm learning Express / Node. I'm trying to build a simple HTML form which I validate client - side (e.g, with jQuery / AJAX) but I also serve from the server - side (with Express.js). To be clear, the app is now simple enough that I could do everything client - side, but I want to build server - side behavior to learn the entire pipeline.
Here's the form. Note the button of type button with name change-color; this is a button that I use for debugging purposes, explained later. I am also including jquery v.3.6.0 and my own client.js file.
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>A MWE</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>A simple form.</h1>
<form action="/" method="post">
<input type="text" name="num1" placeholder = "First Number" value=""> <br>
<input type="text" name="num2" placeholder = "Second Number" value=""> <br>
<button type="submit" name="submit">Calculate</button>
<button type="button" name="change-color">Change color</button>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="client.js" charset="utf-8"></script>
</body>
</html>
In the form, the user is supposed to give me two numbers, which the server will then sum and return. Server code is inside a file called server.js, outlined below:
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({extended:true}));
app.listen(3000, () => {
console.log("Server spun on port 3000.");
});
app.get("/", (req, res) => {
console.log("Serving a GET request at root route.");
res.sendFile(absoluteDirPath() + "index.html");
});
app.post("/", (req, res) => {
console.log("Serving a POST request at root route.");
const num1 = Number(req.body.num1);
const num2 = Number(req.body.num2);
res.send("<h3>Result: " + (num1 + num2) + "</h3>");
});
function absoluteDirPath() {
const path = require('path');
return __dirname + path.sep;
}
When we first access the root endpoint (GET call), the server uses res.sendFile() to send the above HTML file to the user. When the user inputs two numbers on the form and clicks on the button of type submit inside the form, the app.post() of server.js method calculates and send()s the requested sum.
Clearly I need to do some validation before I apply the summation; what if I don't get a number? Based on the answers to this post, to avoid complex and sudden redirections, I seem to have to do this on the client side, e.g, in a separate .js file. In the same post, I have been advised to author something like the following function and assign it to an event listener:
function submitFormWithoutReloading() {
$.ajax({
type: "POST",
url: "/",
data: {
num1: $('#idOfYourFirstInput').val(),
num2: $('#idOfYourSecondInput').val()
},
success: function(res) {
alert(res); // This is what the server returns
}
});
}
However, I seem to have stumbled upon a bigger issue here. I am having some trouble running all my jQuery when I serve the user with the HTML file from the app.get() method! It seems as if every time I access http://localhost:3000/, the HTML file is sent to me, but the linked client.js file is not read (and I am guessing that the same thing holds for jQuery).
To ensure that this is what my problem is, I temporarily forgot about the validation routine, made the change-color HTML button mentioned above and added some dummy code to client.js:
$(document).ready(function() {
alert("Hi!");
});
$('button[name="change-color"]').click(function() {
$("body").toggleClass("colored-background");
});
So the change-color button is used just to toggle the body element to have yellow as its background as per this CSS rule:
.colored-background {
background-color: yellow;
}
If I open index.html from my filesystem, it all works just fine; jQuery and my client.js file are loaded, the alert() comes just fine, and the button works as intended. But when I access localhost:3000 and the GET endpoint serves the client with the HTML file, it's as if the DOM scanning does not read client.js and I can't do any client - side stuff, including my much desired validation!
Any ideas of what I'm doing wrong? I know I could do everything on the client side here, but I want to learn how to split the concerns on client side and server side. Server side might want to access a DB, the client can't do everything.
// Edit: If it helps, the Chrome console says some interesting things when I load localhost:3000:
You didn't do anything wrong. This is just a common problem that everyone using node.js (including me! Cant access other files in Node.js).
You have to tell express which files can be viewed by the client. Let's create a folder called public. Inside your folder, put you .js and .css files, as well as other pictures or other assets you want accessible by the client side.
This folder, public, on your website will be replaced with your domain. Consider these files/folders:
index.js (server)
/ views
- index.html
/ public
- style.css
- client.js
- jquery.min.js
The client can only access the files inside public, like this: http://localhost:3000/style.css, or http://localhost:3000/client.js, or http://localhost:3000/jquery.min.js. However, we will not be able to access the other files (not in the public folder), unless the server explicitly gets it:
app.get('/index.html', function(req, res) {
res.sendFile(path.join(__dirname, '/views/index.html'));
});
Solution:
// ...
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, '/public')));
// You routes here
This way, the client will be able to access all the files under the public directory. Your problem should be solved.
Keep in mind: The server can still access all the files. This only restricts the client side.

res.sendfile in Node Express with passing data along

Is there any way to redirect to an HTML file from a Node.JS application with something like: res.sendFile of express and pass a JSON data along to the html file?
I know this is late but I wanted to offer a solution which no one else has provided. This solution allows a file to be streamed to the response while still allowing you to modify the contents without needing a templating engine or buffering the entire file into memory.
Skip to the bottom if you don't care about "why"
Let me first describe why res.sendFile is so desirable for those who don't know. Since Node is single threaded, it works by performing lots and lots of very small tasks in succession - this includes reading from the file system and replying to an http request. At no point in time does Node just stop what it's doing and read an entire from the file system. It will read a little, do something else, read a little more, do something else. The same goes for replying to an http request and most other operations in Node (unless you explicitly use the sync version of an operation - such as readFileSync - don't do that if you can help it, seriously, don't - it's selfish).
Consider a scenario where 10 users make a request for for the same file. The inefficient thing to do would be to load the entire file into memory and then send the file using res.send(). Even though it's the same file, the file would be loaded into memory 10 separate times before being sent to the browser. The garbage collector would then need to clean up this mess after each request. The code would be innocently written like this:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
That seems right, and it works, but it's terribly inefficient. Since we know that Node does things in small chunks, the best thing to do would be to send the small chunks of data to the browser as they are being read from the file system. The chunks are never stored in memory and your server can now handle orders of magnitude more traffic. This concept is called streaming, and it's what res.sendFile does - it streams the file directly to the user from the file system and keeps the memory free for more important things. Here's how it looks if you were to do it manually:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
Solution
If you would like to continue streaming a file to the user while making slight modifications to it, then this solution is for you. Please note, this is not a replacement for a templating engine but should rather be used to make small changes to a file as it is being streamed. The code below will append a small script tag with data to the body of an HTML page. It also shows how to prepend or append content to an http response stream:
NOTE: as mentioned in the comments, the original solution could have an edge case where this would fail. For fix this, I have added the new-line package to ensure data chunks are emitted at new lines.
const Transform = require('stream').Transform;
const parser = new Transform();
const newLineStream = require('new-line');
parser._transform = function(data, encoding, done) {
let str = data.toString();
str = str.replace('<html>', '<!-- Begin stream -->\n<html>');
str = str.replace('</body>', '<script>var data = {"foo": "bar"};</script>\n</body>\n<!-- End stream -->');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
fs
.createReadStream('../public/index.html')
.pipe(newLineStream())
.pipe(parser)
.pipe(res);
});
You get one response from a given request. You can either combine multiple things into one response or require the client to make separate requests to get separate things.
If what you're trying to do is to take an HTML file and modify it by inserting some JSON into it, then you can't use just res.sendFile() because that just reads a file from disk or cache and directly streams it as the response, offering no opportunity to modify it.
The more common way of doing this is to use a template system that lets you insert things into an HTML file (usually replacing special tags with your own data). There are literally hundreds of template systems and many that support node.js. Common choices for node.js are Jade (Pug), Handlebars, Ember, Dust, EJS, Mustache.
Or, if you really wanted to do so, you could read the HTML file into memory, use some sort of .replace() operation on it to insert your own data and then res.send() the resulting changed file.
Well, it's kinda old, but I didn't see any sufficient answer, except for "why not". You DO have way to pass parameters IN static file. And that's quite easy. Consider following code on your origin (using express):
let data = fs.readFileSync('yourPage.html', 'utf8');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
Now for example, just set a cookie, in yourPage.html, something like:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
And you can plainly pull content of uniqueData from yourCookieName wherever you want in your js
I think the answer posted by Ryan Wheale is the best solution if you actually want to modify something within an HTML file. You could also use cheerio for working with complex logic.
But in regards to this particular question where we just want to pass some data to the client from the server, there's actually no need to read index.html into memory at all.
You can simply add the following script tag somewhere at the top of your HTML file:
<script src="data.js"></script>
And then let Express serve that file with whatever data needed:
app.get("/data.js", function (req, res) {
res.send('window.SERVER_DATA={"some":"thing"}');
});
This data can then easily be referenced anywhere in your client application using the window object as: window.SERVER_DATA.some
Additional context for a React frontend:
This approach is especially useful during development if your client and server are running on different ports such as in the case of create-react-app because the proxied server can always respond to the request for data.js but when you're inserting something into index.html using Express then you always need to have your production build of index.html ready before inserting any data into it.
Why not just read the file, apply transformations and then set up the route in the callback?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
Note that you can't dynamically change data, you'd have to restart the node instance.
You only have one response you can return from the server. The most common thing to do would be to template your file on the server with nunjucks or jade. Another choice is to render the file on the client and then to use javascript to make an ajax call to the server to get additional data. I suppose you could also set some data in a cookie and then read that on the client side via javascript as well.
(Unless you want to template the html file to insert the json data into a script tag). You'll need to expose an api endpoint in express the send along the data to the page, and have a function on the page to access it. for example,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
Now on the client side you can create a request to access this endpoint
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
EDIT:
So arrow functions probably don't work client side yet. make sure to replace them with function(){} in your real code
This is pretty easy to do using cookies. Simply do this:
On the server side -
response.append('Set-Cookie', 'LandingPage=' + landingPageCode);
response.sendFile(__dirname + '/mobileapps.html');
On client side -
<!DOCTYPE html>
<html>
<body onload="showDeferredLandingPageCode()">
<h2>Universal Link Mobile Apps Page</h2>
<p>This html page is used to demostrate deferred deeplinking with iOS</p>
</body>
<script language="javascript">
function showDeferredLandingPageCode() {
alert(document.cookie);
}
</script>
</html>

Can $http.put write data to JSON file

I apologize for the newbie question but I am getting conflicting answers to this while searching on the net.
I have created an AngularJS app to read from a JSON file using $http.get and display the data as a form with each form element binded with ng-model. Ideally I would like the user to be able to edit the desired field and click save, then have that data updated in the JSON file. I have been told that to do this you will need a 3rd party server like NodeJS, but I am seeing other examples that show it being done in videos. Can someone tell me if this is possible without the 3rd party server and if so what is the best practice for doing this.
Thank you
$http GET (for resource located on client) will not work with the Chrome browser and will give a CORS error (unless you disable Chrome web security by opening Chrome using run .../chrome.exe" --allow-file-access-from-files -disable-web-security). Firefox gives an error saying the JSON in not well formed even though it is. Browsers don't seem to like it.
HTML5 LocalStorage is your best bet for client storage where you wish to perform CRUD operations and have the data survive past page refresh. A good example of this is the [TodoMVC example]
(https://github.com/tastejs/todomvc/tree/gh-pages/architecture-examples/angularjs)
A very simple example that saves a json file to localstorage and reads the contents of localstorage is shown. The Service contains a getter and a setter method to interact with localstorage.
INDEX.HTML
<body ng-app = "app">
<div ng-controller="MainCtrl">
<form>
<input placeholder="Enter Name.." ng-model="newContact"/>
<button type="submit" class="btn btn-primary btn-lg"
ng-click="addContact(newContact)">Add
</button>
</form>
<div ng-repeat="contact in contacts track by $index">
{{contact.name}}
</div>
</div>
APP.JS
angular.module('app', ['app.services'] )
.controller('MainCtrl', function ($scope, html5LocalStorage) {
//create variable to hold the JSON
var contacts = $scope.contacts = html5LocalStorage.get();
$scope.addContact = function(contact) {
$scope.contacts.push( {"name":contact} ); //Add new value
html5LocalStorage.put($scope.contacts); //save contacts to local storeage
}
});
SERVICES.JS
angular.module('app.services', [] )
.factory('html5LocalStorage', function () {
var STORAGE_ID = 'localStorageWith_nG_KEY'; //the Local storage Key
return {
//Get the localstorage value
get: function ()
{
return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
},
//Set the localstorage Value
put: function (values)
{
localStorage.setItem(STORAGE_ID, JSON.stringify(values));
}
};
});
Otherwise you could use Node and Express and store the JSON file on the server. Use file system module 'fs-extra' to interact with the json file.
You would have to create RESTful API routes for the client to interact with the server using $http and perform CRUD operations.
/put
/get
/delete
/post
I would be interested to see these videos of this being done without the server writing to the file. You cant just "post the .json file" and have it replace the old one, unless you configure your server (apache, nginx, tomcat, node) to do so.

Simple routing and unobtrusive template engine in Node JS

This is a multi part question and I'm a complete newbie to Node so please be gentle:)
I have a very simple Node/express app set up returning an index.html without using routing...
var app = express();
var port = process.env.PORT || 1337;
app.use('/i', express.static(__dirname + '/i'));
app.use('/Stylesheets', express.static(__dirname + '/Stylesheets'));
app.use(express.static(__dirname));
app.listen(port);
console.log('listening on port ' + port);
The index.html is served as a static file.
My next job is to start returning a few pages with proper routing, I've got as far as working out I need to put my routes in a routes.js file and "require" that file in my server.js file but I can't get my head around setting the routes up and every example/demo I see online seems to do it a different way. Any definitive examples of how to do this would really be appreciated.
The next part of the problem is that I want to include dynamic pages but don't know where to go with templating engines. I would like to use something "unobtrusive" so that my original HTML files still make sense when viewed in a browser.
On the front-end I would simply inject HTML into the page by first using a selector and then using the .html() method to alter the html, I could bind JSON data with a template and then inject it into the right place by looking for a classname etc. THis would be totally unobtrusive and wouldn't require any ugly {} brackets, inline javascript or directives. Psuedo code...
var data = {"name":"John"};
var result = templateEngine.bind("/template.html", data)
$('.person').html(result);
That way, I could keep my original HTML clean and viewable, like this...
<div class="person">
My Name is FirstName
</div>
The closest thing I can find is PURE - http://beebole.com/pure - but I'm not sure how to get it working with NODE (or even if it's compatible).
To add more complexity, whatever templating engine I use needs to be able to use sub-templates(partials?) so that I can include a header/footer etc which is te same on every page. I assume this can be done recursively by referencing sub-templates from within each main template where needed?
If you're still reading this then clearly you'll have worked out that I'm floundering here with a new technology and any help would be really appreciated!
but I can't get my head around setting the routes up and every
example/demo I see online seems to do it a different way. Any
definitive examples of how to do this would really be appreciated.
Not sure what you have seen different in the examples, but the general pattern is like this:
app.**[HTTP VERB]**(**[URL]**, function(req, res){
res.end('<html>hello world</html>');
});
The following code will accept all HTTP GET requests to the root URL of your site:
app.get('/', function(req, res){
res.end('<html>hello world</html>');
});
While the following code will accept all HTTP GET request to /test in your site
app.get('/test', function(req, res){
res.end('<html>hello world from the test folder</html>');
});
It's common to have a separate route for HTTP POST requests (e.g. when the user submits data back to the server). In this case the HTTP verb is POST as in the following example.
app.post('/test', function(req, res){
res.end('<html>Thanks for submitting your info</html>');
});
In this case I am embedding the code to handle the request directly rather than referencing an external routes.js as you indicated just to make the examples cleaner in this question. In a real application you'll do it by referencing an external function so that your app.js stays lean and clean.
app.get('/test', routes.NameOfFunctionToHandleGetForSlashTest);
app.post('/test', routes.NameOfFunctionToHandlePostForSlashTest);
I know this is an old question, but I have explored this topic recently, and came up with the following solution:
my original question
I have placed the following configuration on ejs:
var ejs = require('ejs');
server.configure(function(){
server.set("view options", {layout: false});
server.engine('html', require('ejs').renderFile);
server.use(server.router);
server.set('view engine', 'html');
server.set('views', __dirname + "/www");
});
This sets your view engine as ejs, your view directory as your static public html directory and tells ejs to process .html files as opposed to .ejs.
The routes can be handles like this:
server.all("*", function(req, res, next) {
var request = req.params[0];
if((request.substr(0, 1) === "/")&&(request.substr(request.length - 4) === "html")) {
request = request.substr(1);
res.render(request);
} else {
next();
}
});
server.use(express.static(__dirname + '/www'));
This routes all html requests through the view engine, and passes all other requests on down the stack to be sent as static files.
Your html can now look something like:
<%include head.html%>
<%include header.html%>
<p class="well">Hello world!</p>
<%include footer.html%>
you can have nested includes, and pass variables down into your includes. So for instance your include head can call:
<title> <%= title %> </title>
and at the top of your index page you can include an object like:
{var title: "Home"}
Anyway, maybe this will help out someone who is looking for a ultra easy way to handle includes while sticking with normal html.

Categories