restana-static, serving the frontend with Node.js beyond Nginx

Do not use Node.js to serve static files, it is not efficient. You should use Nginx!

How many times I have heard that, and you? The proper answer is:
Well, depends!

In this article we are gonna describe how we can efficiently serve static files using Node.js. Specially in the context of frontend applications.

Docker containers?

Nowadays we certainly serve frontend applications using Docker and there are many good reasons to do it: The 6 Benefits of Docker Containers

This means, after building the frontend applications we pack the generated static files inside a versioned docker image and deploy it. In this context, the files state never change.

Our frontend demo

For the sake of simplicity in this article, we will use a very simple HTML frontend:

A single “index.html” file under the “src” directory:

<html>
<header>
    <title>Super Web!</title>
</header>
<body>
    Hello world!
</body>
</html>

Serving static files with restana and the serve-static middleware

Let’s implement a Node.js server to serve the frontend static files using restana and serve-static.

Basic setup, loading files from the file-system

const files = require('serve-static')
const path = require('path')

// serving "src" as root directory
const serve = files(path.join(__dirname, 'src')) 

const app = require('restana')({ 
  disableResponseEvent: true 
})

app.use(serve)

app.start(3000)

Default setup of “serve-static” enables HTTP caching using Last-Modified and ETag headers, by doing this we reduce network overhead by keeping copies of the files on the browser cache.

Available configuration options and advance uses cases are described here: https://www.npmjs.com/package/serve-static

Serving files from RAM

As we have mentioned, in case you are using docker containers to expose your frontend applications, you might also have the fact that your files will never change. In this scenario, we can serve the files directly from RAM and deliver maximum performance:

const files = require('serve-static')
const path = require('path')
const cache = require('http-cache-middleware')

const serve = files(path.join(__dirname, 'src'), {
  lastModified: false,
  setHeaders: (res, path) => {
    res.setHeader('cache-control', 'public, no-cache, max-age=604800')
  }
})

const app = require('restana')({
  disableResponseEvent: true
})
app.use(cache())
app.use(serve)

app.start(3000)

This application will serve the static files from RAM for the period of “1 week” and also enable HTTP cache on the client browser. Advanced use cases for the http-cache-middleware module are available here: https://www.npmjs.com/package/http-cache-middleware

Don’t worry, by using the no-cache directive in the Cache-Control header, we tell the browsers to validate the cache state with the server before delivering cached content. So if you deploy a new version of your frontend application (meaning a new docker image), the new content will be served immediately as expected.

restana-static vs Nginx

How does this alternatives compare to Nginx?

NGINX is open source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. It started out as a web server designed for maximum performance and stability. In addition to its HTTP server capabilities, NGINX can also function as a proxy server for email (IMAP, POP3, and SMTP) and a reverse proxy and load balancer for HTTP, TCP, and UDP servers.

https://www.nginx.com/

Here a basic benchmark on a MacBook Pro 2016, 2,7 GHz Intel Core i7, 16 GB 2133 MHz LPDDR3 using:

wrk -t8 -c8 -d20s http://127.0.0.1:3000/index.html

In this tests we used a single instance of the HTTP servers, on cluster mode, numbers should be higher.

Conclusions

In this article we have described how we can efficiently serve static files (aka frontends) using Node.js + restana + serve-static.
We have quickly mention browser caching integration and a performance comparison vs Nginx.

Don’t get me wrong, Nginx is great and very mature! But if you are an “all things JavaScript” guy as I am, and you also want more freedom and control of your code, you can give it a try, Node.js is also great at it!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.