#node-static-asset
node-static-asset is a static asset manager for Node.JS, designed for Express.
This project aims to solve the problem of caching static assets (including
assets like *.js files that might change from time to time).
Background
Google has a nice article about "strong" and "weak" caching. It's worth a quick
read if you don't know what that means.
https://developers.google.com/speed/docs/best-practices/caching
Install
npm install static-asset
Getting Started
node-static-asset allows you to generate URL fingerprints for static assets.
- Add the static-asset middleware to your Express stack
var staticAsset = require('static-asset');
app.use(staticAsset(__dirname + "/public/") );
- Get URL fingerprints of your static resources using
req.assetFingerprint
or the assetFingerprint
view helper function.
app.get("/info", function(req, res, next) {
res.type("text/plain").send("The URL fingerprint for jQuery is: " +
req.assetFingerprint("/js/jquery.min.js") );
});
Now that you have obtained the asset fingerprint for /js/jquery.min.js
,
if you send a request for that asset to the URL /js/jquery.min.js?v=3dd-983jk2a
,
static-asset will automatically add appropriate caching headers (i.e.
Last-Modified, ETag, and Expires).
API
static-asset exposes a function req.assetFingerprint
, which allows one to generate
and register URL fingerprints for static assets.
Once a URL fingerprint is registered with static-asset, any HTTP request for that
static asset (i.e. when req.url
matches the registered URL fingerprint) will
trigger static-asset to set certain HTTP headers (i.e. Last-Modified, ETag, or
Expires).
Adding the middleware function
require('static-asset')(path[, cache]) - Returns an Express middleware
function that exposes a req.assetFingerprint
function and adds
assetFingerprint
view helper function to res.locals
. If any request's URL
matches a previously generated URL fingerprint, static-asset will attempt to add
weak and strong caching headers to the HTTP response.
path
- the path from which static files are servedcache
- a "cache strategy" Object, which must implement all "cache
strategy" methods, as described below. If cache
is omitted, the
default "cache stategy" is used.
A "Cache Strategy" Object
A "cache strategy" object should implement one or more of the following methods:
lastModified(filename)
- a function that accepts a filename and returns
its last modified date. If a last modified date could not
be determined, the function should return null
; otherwise, static-asset
may use this Date to set the Last-Modified
HTTP response header when
the resource is requested.etag(filename, cb)
- Same as lastModified (above), except that it must
return an ETag (or hash value). If the
returned ETag is not null
, static-asset may use this value to set the
ETag
HTTP header when the named resource is requested.expires(filename)
- Same as lastModified (above), except
that it must return a Date Object indicating when the resource shall
expire. The Date may be no more than one year in the future. If
expires
is implemented, static-asset may use the date to set an
Expires
and/or Cache-Control: max-age
HTTP headers; otherwise,
static-asset will use a Date approximately one year into the future.fileFingerprint(filename, fullPath)
- Returns the URL fingerprint
of the resource filename
, stored at the location fullPath
.
fullPath
is provided for convenience, since the caching strategy
does not know what root path was passed into the static-asset middleware.
Registering URL fingerprints
req.assetFingerprint(label_or_filename) - Return a URL fingerprint for the
labelled resource, or if no such label is registered, use the "cache
strategy" to determine the file's ETag or last modified date. If you're confused
by this description, read on...
If you call req.assetFingerprint(filename)
and pass a filename relative to the
path from which static files are served, static-asset will use the cache strategy
you specified to generate and return a unique URL fingerprint for the asset.
If that file is requested later by the URL fingerprint, static-asset will respond
by setting the appropriate HTTP headers like Last-Modified, ETag, and Expires,
according to the cache strategy.
If an ETag is provided by the cache strategy, it will be used to generate the
fingerprint; otherwise, the last modified date will be used.
If you call req.assetFingerprint()
with no arguments, a fingerprint will be
registered and generated using the cache strategy on the current URL (i.e.
req.url
). This is equivalent to req.assetFingerprint(req.url)
.
Labelled resources
You can call req.assetFingerprint(label, fingerprint, cacheInfo)
to manually
assign a fingerprint
for the specified label
. In addition, the HTTP headers
returned to the client when this URL fingerprint is requested are specified by
the cacheInfo
Object.
req.assetFingerprint(label, urlFingerprint, cacheInfo) - Registers a URL
fingerprint for a labelled resource.
label
- a label identifying the resourceurlFingerprint
- the URL fingerprint for the resource. If a request for this
resource is made, static-asset may add caching headers to the response.cacheInfo
- an Object containing one or more of these properties:
lastModified
- the last modified date of the resourceetag
- the ETag of the resourceexpires
- the expiration date of the resource
Other middleware on the stack can generate their own URL fingerprints for
static resources and expose them through req.assetFingerprint
. Like this:
req.assetFingerprint(javascript_filename, javascript_filename + "?v=" +
stat.mdate.getTime(), {"lastModified": stat.mdate});
If you call req.assetFingerprint(label)
and pass a label, static-asset will return
the fingerprint for the resource, as specified by the last corresponding
req.assetFingerprint(label, fingerprint, cacheInfo)
call. If that file is requested,
static-asset will respond by setting the appropriate HTTP headers, according to the
cacheInfo
Object passed to the last
req.assetFingerprint(label, fingerprint, cacheInfo)
call.
Default Caching Strategy
static-asset can be fully customized, but it has some basic, reasonably sane default behavior.
By default, static-asset does the following:
- The URL fingerprint of the resource is based on the ETag
- The ETag is generated based upon the file size and the file's CRC-32 hash.
- The last modified date is pulled from
fs.stat
- The expires date is set to one year in the future
- The default strategy relies on [
connect.static
]
(http://www.senchalabs.org/connect/middleware-static.html) to load the resource from
the filesystem. - In development environments (based on
process.env.NODE_ENV
), the URL fingerprint
will be updated whenever the file changes - In production environments, the URL fingerprints are cached and cannot
change until the server is restarted.
Basic Usage
Usually, this should be good enough to get started.
var express = require('express');
var app = express();
var staticAsset = require('static-asset');
app.use(staticAsset(__dirname + "/public/") );
app.use(express.static(__dirname + "/public/") );
For example, if you want to include your client-side JavaScript code, simply
do this in your Jade or [Blade]
(https://github.com/bminer/node-blade) view:
script(type="text/javascript", src=assetFingerprint("/client.js") )
This will render to something like this:
<script type="text/javascript" src="/client.js?v=1318365481"></script>
Notice that static-asset added a URL fingerprint (the UNIX timestamp
1318365481) to the filename.
More Advanced Usage
You can override the "cache strategy" with your own implementation that might
allow you to:
- Upload the asset to Amazon S3 and generate a URL fingerprint that points to S3
- Fly in a spaceship to the moon
- Do something really crazy like generate URL fingerprints that are
Base64-encoded MD5-hashes of the names of random lunar craters.