Thursday, November 10, 2011: Gzipping Responses in Node.js + Express App (Obsoleted)
Not long ago while developing the #thaiWitter 3 server (Node.js + Express App), I realized that Heroku/Cedar doesn’t gzip the output, so I need to gzip them myself. I go with node-compress.
Update 2012-06-25: Since node 0.6 we now have Zlib built-in, so
this post is no longer valid. Just use(connect.compress())
hell yeah!
Gzipping Static Content
My app has a lot of client side data, and they are served using express.static
.
gzippo is a drop-in replacement for it, so just install gzippo and use gzippo.staticGzip
in place of express.static
. It gzips the static data on-the-fly and cache them in memory.
My Gzip Middleware
I also need to Gzip the API output too, so I made my own middleware express-gzip. And the code that does the action here.
It doesn’t get in your way, that means, you have to tell it to output Gzip yourself, but that’s easier for me. It adds 3 functions. 1 to the request object, and 2 to the response object.
req.supportsGzip()
- returns true if the browser supports Gzipres.setGzipHeader()
- sets the gzip headers for the responseres.sendGzipped(text, headers)
- sends text data to the browser, gzipped
Note that res.sendGzipped()
only allow UTF-8 encoded strings.
I’m going to use it in the rest of this post.
Gzipping API Responses
As a web-based Twitter client, most of my API calls returns data from Twitter API as JSON. And JSON gzips very well!
For that, at the final stage when sending output, I just use res.sendGzipped
from my middleware.
Gzipping Pre-gzipped Assets
The client-side JavaScript file is big, nearly 190k.
I have a build script to Gzip them, and I use a separate request handler to load both the non-gzipped version and gzipped version into memory at startup time.
I need to do that because when I compile assets, it goes to, thaiWitterAsset1.js
. But I also need a way to make the browser get the latest version. So I reference in the HTML file as thaiWitterAsset1_[hash of the file].js
. express.static
couldn’t do that, so I need to make a new request handler. Surely I could just append a query string, but I heard from somewhere that it will not be cached as good as having the hash in the file name.
The handler code looks something like this:
if (req.supportsGzip()) {
res.setGzipHeader();
return res.send(gzipped, headers);
} else {
return res.send(nonGzipped, headers);
}
Performances
So people at Connect and Express said that it is more of a loss to Gzip. It takes lot of power to serve Gzipped content and removed it in newer versions.
But for me, my connection to Heroku wasn’t fast. So before I use Gzip, the home timeline loads in about 2.3s, but after I enable Gzip, it loads in about 1.5s. I didn’t remember the number, but it’s significantly faster.
The size of the JSON output from the server reduced from 45k to around 8k and that makes it load faster.
add / view all comments
Responses