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?

- 483
- 1
- 7
- 11
-
1What does "pass data along to the HTML file" mean? – jfriend00 Oct 08 '15 at 22:41
-
You only have one response to render. – Robert Moskal Oct 08 '15 at 22:41
-
1So in HTML file I can use that json data ; JSON data can be user data/info... – user3393991 Oct 08 '15 at 22:42
-
@RobertMoskal does that mean it is not doable? – user3393991 Oct 08 '15 at 22:42
-
1Related: https://stackoverflow.com/questions/35418751/res-sendfile-send-static-file-object – user2314737 Jun 18 '17 at 21:51
-
Alternate approach if you don't have much data to pass along: Send the data in the querystring via a redirect. – Cullub Jul 15 '20 at 12:16
8 Answers
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);
});

- 26,022
- 8
- 76
- 96
-
3Tried this solution for sending json file. I got `Error: write after end` on second refresh. – Ajeet Lakhani May 14 '18 at 09:24
-
But if data is streamed in chunks. It could be that one chunk ends with ``, right? In which case, the replacement of `
` is never carried out.
– Christiaan Westerbeek Jun 08 '19 at 21:21 -
1@ChristiaanWesterbeek - that is a reasonable assumption, though I have found that this never happens. I'm not sure how the internals of data streaming works in node, but I imagine a web server would never emit a partial tag string like that as that could break the incremental rendering / user experience in the browser. – Ryan Wheale Jun 10 '19 at 15:51
-
"the file would be loaded into memory 10 separate times" - it won't be if you load those files before hand into the memory, I'm using Promise.all() to do it, before starting the routes. – Rohit Nair Aug 05 '19 at 13:38
-
@RohitNair - that's the role of a caching layer IMO - something like NGINX, Redis, or a CDN should manage that, not your application server. Keeping things in memory like that might also start to leak in ways that you did not anticipate. I highly advise against doing things that way unless it's for a very small number of small frequently used files. – Ryan Wheale Aug 05 '19 at 16:31
-
1@Ajeet Lakhani: you had to put the the "parser" into the app.use function to generate every time a new Transform instance (or someone knows a reset-stream-way?) – crazyx13th Nov 15 '19 at 19:31
-
Christiaan's comment is exactly right. You don't control what chunks you are going to get when streaming so you cannot rely on the fact that a tag won't be split across a chunk. Plus, this concept of replacing something in an HTML file with data is essentially what 20 template engines already do, but they do it in a much more capable way so there's really no reason to reinvent such a system when more capable ones are waiting for you to use them. Plus, this is likely to be buggy with the chunk boundaries. – jfriend00 Dec 19 '19 at 15:50
-
@jfriend00 - Thanks for the feedback. I think a templating engine serves a different purpose than what the OP was looking for. My solution could be easily improved to make sure that chunks are emitted at new lines, in which case `
` would always match and you still get the benefits of streaming data. But that's an exercise for the user.
– Ryan Wheale Dec 19 '19 at 18:09 -
This works, because `fs.createReadStream()` creates a stream with `highWaterMark` of 64 kilobytes (user-definable). `highWaterMark` determines how much data (approx.) is going to be buffered for consumption. After the data is consumed, it is going to be replaced with new data. Usually, `` is somewhere in the first 64kb of the document and I haven't seen an HTML where it is not the case ;) To change highWaterMark - `fs.createReadStream(file, {highWaterMark: n * 1024})`, where `n` is desirable amount of KBs. For this reason, I think @RyanWheale 's approach is quite good – Nikolay Schamberg Jul 22 '21 at 23:18
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

- 2,203
- 1
- 19
- 37

- 67
- 1
- 2
-
3Sync is BAD. Either use Async version, or much better; Stream. See https://stackoverflow.com/a/38129612/5674976 – George Mar 01 '18 at 15:27
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.

- 994
- 11
- 27
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.

- 384
- 3
- 15
-
Alternatively, make those replaces in the routes instead. Like, res.send(html.replace("DATA", JSON.stringify(data))); – Rohit Nair Aug 05 '19 at 13:40
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.

- 21,737
- 8
- 62
- 86
(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

- 2,480
- 6
- 18
- 25

- 20,499
- 6
- 65
- 53
-
1ES6 code for client-side without any mention of compatibility or preprocessors? – jfriend00 Oct 08 '15 at 22:56
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>

- 3,455
- 28
- 31