Skip to content

Commit 9001e04

Browse files
On-Demand ISR example with Build Output API (vercel#306)
Co-authored-by: JJ Kasper <[email protected]>
1 parent a24aac9 commit 9001e04

File tree

8 files changed

+107
-0
lines changed

8 files changed

+107
-0
lines changed

build-output-api/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ that are listed below.
1616
- [Edge Functions](./edge-functions)
1717
- [Edge Middleware](./edge-middleware)
1818
- [Image Optimization](./image-optimization)
19+
- [On-Demand Incremental Static Regeneration (ISR)](./on-demand-isr)
1920
- [Overrides](./overrides)
2021
- [Prerender Functions](./prerender-functions)
2122
- [Preview Mode](./preview-mode)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"version": 3
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"runtime": "nodejs16.x",
3+
"handler": "index.js",
4+
"launcherType": "Nodejs"
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = (req, res) => {
2+
res.setHeader('Content-Type', 'text/html; charset=utf-8')
3+
4+
// NOTE: the below revalidate link is meant for demo purposes only
5+
// actual authentication should be used in actual projects
6+
res.end(`
7+
<h1>On-Demand Incremental Static Regeneration (ISR) Example</h1>
8+
<p>The "server time" below will only get updated after you click the Revalidate link.</p>
9+
<p>Server time: ${new Date().toISOString()}</p>
10+
<p><a href="/revalidate?authToken=a13f94f6-a441-47ca-95fc-9f44f3450295">Revalidate</a></p>
11+
`)
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"expiration": 180,
3+
"group": 1,
4+
"bypassToken": "87734ad8259d67c3c11747d3e4e112d0",
5+
"allowQuery": []
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"runtime": "nodejs16.x",
3+
"handler": "index.js",
4+
"launcherType": "Nodejs",
5+
"shouldAddHelpers": true
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const https = require('https')
2+
3+
// The bypass token should be a build-time randomly generated string of at least 32 characters.
4+
// This value should only be exposed in the function config and inside of the function itself.
5+
// DO NOT expose this value on the client-side: a malicious user could trigger boundless revalidations.
6+
const bypassToken = '87734ad8259d67c3c11747d3e4e112d0'
7+
8+
// This authToken is a trivial example of authentication, but any strong authentication method will work.
9+
// DO NOT allow unauthenticated users to trigger revalidation: a malicious user could trigger boundless revalidations.
10+
const authToken = 'a13f94f6-a441-47ca-95fc-9f44f3450295';
11+
12+
module.exports = (req, res) => {
13+
const params = new URLSearchParams(req.query);
14+
// Validate the request is allowed to trigger revalidation
15+
if (authToken !== params.get('authToken')) {
16+
res.statusCode = 403
17+
res.end('Not Authorized')
18+
}
19+
20+
const host = req.headers.host
21+
const deployedUrl = `https://${host}`
22+
23+
const options = {
24+
hostname: host,
25+
port: 443,
26+
path: '/',
27+
method: 'GET', // MUST be "GET"; a "HEAD" or "POST" request will not work
28+
headers: {
29+
'x-prerender-revalidate': bypassToken
30+
}
31+
}
32+
const revalidateRequest = https.request(options, revalidateResponse => {
33+
res.setHeader('Content-Type', 'text/html charset=utf-8')
34+
35+
if (revalidateResponse.headers['x-vercel-cache'] !== 'REVALIDATED') {
36+
res.statusCode = 500
37+
res.end(`
38+
<h1>Cache NOT Revalidated!</h1>
39+
<p>Failed to revalidate. The \`x-vercel-cache\` header was not set to \`REVALIDATED\`.</p>
40+
`)
41+
}
42+
43+
res.end(`
44+
<h1>Cache Revalidated!</h1>
45+
<p>Redirecting you back.</p>
46+
<meta http-equiv="refresh" content="2 url=${deployedUrl}">
47+
`)
48+
})
49+
50+
revalidateRequest.on('error', error => {
51+
console.error(error.stack)
52+
res.statusCode = 500
53+
res.end()
54+
})
55+
56+
revalidateRequest.end()
57+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# On-Demand Incremental Static Regeneration (ISR)
2+
3+
## Build Output API
4+
5+
This Prebuilt Deployment example demonstrates how to implement "On-Demand Incremental Static Regeneration (ISR)" when using the [Build Output API](https://vercel.com/docs/build-output-api/v3).
6+
7+
### Demo
8+
9+
https://build-output-api-isr.vercel.sh
10+
11+
### How it Works
12+
13+
When using Prerender Functions, you may want to revalidate the cache for a specific path.
14+
15+
To implement this, the `bypassToken` of the [`<name>.prerender-config.json`](./.vercel/output/config/index.prerender-config.json) file should be set to a randomized string that you generate at build-time. This string should not be exposed to users / the client-side, except under authenticated circumstances.
16+
17+
To revalidate a path to a Prerender Function, make a `GET` request to that path with a header of `x-prerender-revalidate: <bypassToken>`. A `HEAD` request will not work. When that Prerender Function endpoint is accessed with this header set, the cache will be revalidated, bypassing any caching that Vercel would normally provide.

0 commit comments

Comments
 (0)