hapi js and etags

This will be a quick post on etags, and the entity response toolkit method in hapi 17.x. This might be the first of a few posts on hapi, and web cache, but there is all ready some great content out there on the subject with the official hapi docs, which also links to a resource at developers.google.com when it comes to etags and more. Still I thought I would work out some of my own examples when it comes to this. Etags are a way to go about taging a response with a unique value that can be used as a way for a browser to know if it still has an up to date version of the content or not, and thus can still continue to use the cached resource rather than downloading the resource yet again.

1 - Basic etag example in hapi 17.x

So an etag is a way of setting a token for a request for a resource and it is a way of letting the browser know if the resource has changed or not. It can then be a hash of the state of the content itself, or some other string value of sorts that is updated not and then whenever a newer version of the content is available.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
let Hapi = require('@hapi/hapi');
let init = async() => {
let server = Hapi.server({
port: 3000,
host: 'localhost'
});
// etag based on the time the server started
let startTime = new Date(),
etag = startTime.getTime();
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
h.entity({
etag: etag
});
return {
mess: 'date should only change each time the sever starts',
date: new Date()
};
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();

2 - updating an etag

When an etag is updated the browser will download the new content. So an etag is a great way of tagging content to help inform the browser as to the state of the content in terms of when it changed last. So it can potential be used to help reduce the amount of bandwidth used transmitting content out to clients. It is after all kind of silly for clients to keep re downloading content that has not changed after all.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
let Hapi = require('@hapi/hapi'),
fs = require('fs'),
readdir = require('util').promisify(fs.readdir),
dir_folders = process.argv[2] || process.cwd(),
delay = process.argv[3] || 10000;
let init = async() => {
let server = Hapi.server({
port: 3000,
host: 'localhost'
});
// gen etag
let genEtag = (function () {
let c = 0;
return () => {
let startTime = new Date();
c += 1;
c %= 100;
return startTime.getTime() + ':' + c;
};
}
());
// first etag
let etag_server = genEtag();
// update etag each delay
setInterval(() => {
etag_server = genEtag();
}, delay);
// set up the route
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
// using etag_server for each request
h.entity({
etag: etag_server
});
// get client etag
let etag_client = request.headers['if-none-match'];
etag_client = etag_client === undefined ? 0 : etag_client.replace(/"/g, '');
// send response based on client etag
// compared to server etag
if (etag_client === etag_server) {
console.log('request for fresh resource');
return {};
} else {
console.log('old cached copy getting the new stuff...');
return readdir(dir_folders).then((contents) => {
return {
date: new Date(),
etag_server: etag_server,
contents: contents
}
})
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();

3 - Conclusion

That is all for now when it comes to etags in hapi js. I will not be writing new content for hapi for a while until maybe I have some actual project of some kind that I am working on, at which point I will likely update the quality of my content on hapi.