npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vapr-conditionals

v0.2.1

Published

A Vapr plugin for handling conditional requests

Downloads

8

Readme

vapr-conditionals Build Status

Installation

npm install --save vapr
npm install --save vapr-conditionals

Usage

This plugin enables conditional requests. Specifically, it handles If-Match, If-None-Match, If-Modified-Since, and If-Unmodified-Since headers, while providing clients with ETag and Last-Modified headers.

Conditional requests can make your server more efficient by saving bandwidth on responses that don't change very often. Also, they can empower clients to avoid certain race conditions.

When you add this plugin to a route, a new function called req.validate() becomes available. You must call req.validate() exactly once before returning a successful response. When you do, you can provide it with a lastModified date, a weak ETag, or a strong ETag (see below for details). The req.validate() function can throw a 304 Not Modified or a 412 Precondition Failed response, or it can simply return normally, allowing your app to generate its typical (2xx) response.

const conditionals = require('vapr-conditionals');
const app = require('vapr')();
const route = app.get('/foo');

route.use(conditionals());
route.use((req) => {
	req.validate({ lastModified: new Date(someTimestamp) });
	return [[someData]];
});

Any checks that you perform which may cause a 3xx or 4xx response should be done before calling req.validate(). In other words, req.validate() should only be called immediately before generating a successful response (or immediately before perfoming any meaningful actions, in the case of POST, PUT, DELETE, or PATCH requests).

Options

options.lastModified = null

The simplest way to use this plugin is to call req.validate() with the lastModified option, which must be a Date object. The given date should represent the last time that the requested resource was modified (or created). If the requested resource does not exist, you can use null instead of a Date object (or simply call req.validate() without any options).

req.validate({ lastModified: new Date(someTimestamp) });

When you use this approach, a weak ETag header will automatically be generated from the given date, and the Last-Modified header will also be sent as a fallback for older clients that don't support ETags.

Don't use this approach if any of the following statements are true:

  • You'd like to invalidate caches based on something other than the given lastModified date.
  • Your clients need guaranteed data freshness with a precision finer than 1 second.
  • Your clients need to use the If-Match header to avoid certain race conditions.
  • You don't want to support conditional requests for legacy clients that don't support ETags.

options.weak = null

If you need to invalidate caches based on factors other than a lastModified date, you can instead call req.validate() with the weak option, which must be an array of strings and/or Buffers. All data in the array will be hashed and combined to generate a single weak ETag. If any element of the array is different from one request to another, the generated ETags will also be different (which will invalidate caches). If the requested resource does not exist, you can use null instead of an array (or simply call req.validate() without any options).

// ISO strings have millisecond resolution
const isoString = new Date(someTimestamp).toISOString();

req.validate({ weak: [isoString, requestedLanguage] });

When you use this approach, an ETag header will be sent, but the Last-Modified header will not be sent unless you also provide a lastModified date.

Don't use this approach if your clients need to use the If-Match header to avoid certain race conditions.

options.strong = null

If your clients need to use the If-Match header to avoid certain race conditions, you must use strong ETags instead of a weak ones. Strong ETags are much harder to generate correctly, so they're not recommended unless you truly need them. To use strong ETags, call req.validate() with the strong option, which behaves exactly like the weak option.

However, when using the strong option, you must adhere to strict requirements that cannot be enforced by this plugin:

  • A strong ETag must change whenever any observable change to the resource payload changes.
  • A strong ETag must be unique across all versions of a resource over time.
  • A strong ETag must be different for different representations of the same resource. For example, if content negotiation is used to conditionally apply gzip compression (Content-Encoding) to a resource, that resource's ETag must be different between the gzipped and non-gzipped versions. It's a common mistake to forget this. If you apply compression via a plugin (like vapr-compress), you should include the request's Accept-Encoding header in your ETag's array. Keep in mind that this also applies to any other transformations that you apply to the response body after it is generated.

For more details on the requirements of strong ETags, read here.

const crypto = require('crypto');

const hashedPayload = crypto.createHash('md5').update(payload).digest();
const etagParts = [hashedPayload];

if (req.headers.has('accept-encoding')) {
	etagParts.push(req.headers.get('accept-encoding'));
}

req.validate({ strong: etagParts });

When you use this approach, an ETag header will be sent, but the Last-Modified header will not be sent unless you also provide a lastModified date.