A presentation at Decoupled Days 2021 by Phil Hawksworth
Demystifying Incremental Static Regeneration And raising the Jamstack ceiling Phil Hawksworth, Netlify
! @philhawksworth
These slides and links findthat.at/incremental @philhawksworth
Why this talk? Terminology / confusion / doubt / opportunity
Jamstack @philhawksworth
JavaScript / APIs / Markup ( but Jamstack means more than what it stands for ) @philhawksworth
Jamstack A way of thinking about how to build for the web. The UI is compiled, the frontend is decoupled, and data is pulled in as needed. @philhawksworth
Decoupling @philhawksworth
Compiling @philhawksworth
Pre-generating @philhawksworth
It used to be so simple ASK FOR STUFF GET STUFF @philhawksworth
Time and context TRADITIONAL STACK LOAD BALANCERS WEB SERVERS DATABASES JAMSTACK ASSETS ON CDN BUILD AHEAD OF TIME @philhawksworth CODE & CONTENT
Decoupling @philhawksworth
Front-end code is no longer limited to being a product of a back-end system @philhawksworth
Capable of being served directly from a CDN @philhawksworth
Offloading hosting complexity as a solved problem @philhawksworth
Pre-generation of a site + The workflows and automation that unlocks @philhawksworth
T I F E N E B HUGE Deploys are immutable and atomic @philhawksworth
Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Deploy 89 Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Live Deploy 89 Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Deploy 89 Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Deploy 89 Deploy 88 Deploy 87 Live Deploy 86 @philhawksworth Deploy 86
Zero burden on the developers for caching logic
Caching is one of the easiest things in computer science — Nobody ever. @philhawksworth
Confidence & certainty @philhawksworth
But… (And potentially a big but) @philhawksworth
WH ? T U O B A T A User generated content?
WH ? T U O B A T A Sites with huge numbers of pages?
Has Jamstack reached its ceiling? WH ? T U O AT AB Sites with huge numbers of pages? WH ? T U O AT AB User generated content?
Incremental builds @philhawksworth
@ Phil Hawksworth Director of Developer Experience, Netlify
findthat.at/jamstack/book
findthat.at/interesting
These slides and links findthat.at/incremental @philhawksworth
Let’s talk 1 2 Approaches to delivering incremental builds Understanding the benefits and the sacrifices 3 A practical example of a first step to incremental builds
1 Approaches to delivering incremental builds @philhawksworth
S E H C A O R APP ISR Incremental Static Regeneration @philhawksworth / DPR Distributed Persistent Rendering / BYO Bring Your Own
Incremental Static Regeneration (ISR) @philhawksworth
Added to Next.js by Vercel @philhawksworth
@philhawksworth Source: https://www.smashingmagazine.com/2021/04/incremental-static-regeneration-nextjs
When to render? Specifying the fallback fallback: false fallback: true fallback: blocking If the page was not generated in the build, request will 404 Serve a stale page or holding page but update the cache for future requests Generate a page ondemand and cache it for future @philhawksworth
An excellent enhancement to Next.js Managing caching behaviours needs careful handling @philhawksworth
EARLI ER Distributed Persistent Rendering (DPR) @philhawksworth
Distributed Persistent Rendering (DPR) @philhawksworth
(Builds with a long tail) findthat.at/dpr @philhawksworth
Code @philhawksworth
Code Build Deploy number 75 @philhawksworth
Code Build Deploy number 75 @philhawksworth On demand
Code Build Deploy number 75 @philhawksworth On demand
Code Build Deploy number 75 @philhawksworth On demand
Code Build Deploy number 76 @philhawksworth
Code Build Deploy number 76 @philhawksworth On demand
Code Build Deploy number 76 @philhawksworth On demand
Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Deploy 89 Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Live Deploy 89 Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
Deploy 89 Live Deploy 88 Deploy 87 Deploy 86 @philhawksworth Deploy 86
A serverless function const handler = async event =>” { // return a view }; @philhawksworth
A serverless function const { builder } = require(“@netlify/functions”); const handler = async event =>” { // return a view }; exports.handler = builder(handler); @philhawksworth
Help to refine this pattern findthat.at/dpr/rfc findthat.at/dpr/csstricks @philhawksworth
Bring Your Own What are the underlying primitives you need? @philhawksworth
Use with any generator or framework @philhawksworth
A site without complex interdependencies between pages
lolly.net thousands of pages of user content Each new deploy comes from the build cache And then generates new pages and adds them to the build cache @philhawksworth
2 Understanding the benefits and the sacrifices @philhawksworth
Generating pages on-demand S G N BRI The ability to serve far larger sites The ability to deliver previously unknown, dynamic content @philhawksworth
BUT Generating pages on-demand Can re-introduce the uncertainty of what lives at a URL Can violate the contract of atomic, immutable builds Can make architectures harder to reason about @philhawksworth
So consider your approach carefully Evaluate the functional and non-functional requirements of your project @philhawksworth
3 A practical example of a first step to incremental builds @philhawksworth
findthat.at findthat.at/thecode
_redirects /thecode /incremental /supasupa /magic-roundabout /learn/graphql /eleventail /cal /jamstack/london /jamstack/growing @philhawksworth https://github.com/philhawksworth/findthat https://noti.st/philhawksworth https://www.netlify.com/blog/2021/06/28/sa https://en.wikipedia.org/wiki/Magic_Rounda https://graphql.org/learn/ https://www.hawksworx.com/blog/eleventailhttps://calendar.google.com/calendar/u/0/r https://app.experiencewelcome.com/events/9 https://noti.st/philhawksworth/5Zh3rm/jams
BEH S R U AVIO Render the page the first time it is requested Persist the page as part of the latest deploy Start fresh after each new deploy @philhawksworth
We’ll need 1 2 A function to build the pages on-demand and persist them A way to direct requests to our function
_redirects /magic-roundabout /learn/graphql /eleventail /cal /jamstack/london /jamstack/growing https://en.wikipedia.org/wiki/Magic_Rounda https://graphql.org/learn/ https://www.hawksworx.com/blog/eleventailhttps://calendar.google.com/calendar/u/0/r https://app.experiencewelcome.com/events/9 https://noti.st/philhawksworth/5Zh3rm/jams /qr/*$ /.netlify/functions/show-qr @philhawksworth 200
show-qr.js const const const const const
{ builder } QRCode fetch pageTemplate rootURL
= = = = =
require(“@netlify/functions”); require(‘qrcode’); require(‘node-fetch’); require(‘../../includes/page.js’); “https://findthat.at”;
const handler = async event =>” { // Get the original short URL (without the qr part of the path) const path = event.path.split(“/qr/”)[1]; const shortURL = ${rootURL}/${path}
; // follow the redirect to get the destination to display const destinationURL = await fetch(shortURL); // make a QR cade and then return a page displaying it return QRCode.toString(shortURL, {‘type’:’svg’} ) .then(svg =>” { // render the data into the template return { statusCode: 200, body: pageTemplate({ shortURL : shortURL, destinationURL : destinationURL.url, svg: escape(svg) }) };
}) .catch(err =>” { console.error(err) })
};
exports.handler = builder(handler);
@philhawksworth
show-qr.js const handler = async event =>” { // Get the original short URL (without the qr part of the path) const path = event.path.split(“/qr/”)[1]; const shortURL = ${rootURL}/${path}
; // follow the redirect to get the destination to display const destinationURL = await fetch(shortURL); // make a QR cade and then return a page displaying it return QRCode.toString(shortURL, {‘type’:’svg’} ) .then(svg =>” { // render the data into the template return { statusCode: 200, body: pageTemplate({ shortURL : shortURL, destinationURL : destinationURL.url, svg: escape(svg) }) };
}) .catch(err =>” { console.error(err) })
};
@philhawksworth
show-qr.js const const const const const { builder } QRCode fetch pageTemplate rootURL @philhawksworth = = = = = require(“@netlify/functions”); require(‘qrcode’); require(‘node-fetch’); require(‘../../includes/page.js’); “https://findthat.at”;
page.js
module.exports = (data) =>” { return <!DOCTYPE html> … <body> <header> <h1><a href="${ data.shortURL }">${ data.shortURL }</a></h1> <a class="dest" href="${ data.destinationURL }">${ data.destinationURL }</a> </header> <main> <img src='data:image/svg+xml;utf8,${ data.svg }'> </main> …
;}
@philhawksworth
show-qr.js const const const const const
{ builder } QRCode fetch pageTemplate rootURL
= = = = =
require(“@netlify/functions”); require(‘qrcode’); require(‘node-fetch’); require(‘../../includes/page.js’); “https://findthat.at”;
const handler = async event =>” { // Get the original short URL (without the qr part of the path) const path = event.path.split(“/qr/”)[1]; const shortURL = ${rootURL}/${path}
; // follow the redirect to get the destination to display const destinationURL = await fetch(shortURL); // make a QR cade and then return a page displaying it return QRCode.toString(shortURL, {‘type’:’svg’} ) .then(svg =>” { // render the data into the template return { statusCode: 200, body: pageTemplate({ shortURL : shortURL, destinationURL : destinationURL.url, svg: escape(svg) }) };
}) .catch(err =>” { console.error(err) })
};
exports.handler = builder(handler);
@philhawksworth
show-qr.js const { builder } = require(“@netlify/functions”); const handler = async event =>” { … }; exports.handler = builder(handler); @philhawksworth
These slides and links findthat.at/incremental findthat.at/qr/incremental @philhawksworth
findthat.at/qr/incremental
Wrapping up @philhawksworth
R E B M E M RE Incremental builds are possible right now Sites built with any framework or generator can be enhanced Embracing the law of least power helps protect an architecture you can reason about @philhawksworth
R E B M E M RE The benefits of a Jamstack architecture are worth protecting Examine your use cases Please don’t make me have to understand and manage end-to-end caching @philhawksworth
Jamstack Community Survey 2021 findthat.at/jamstack/survey Jamstack Conf - Speak! jamstackconf.com/cfp
Thanks @philhawksworth findthat.at/incremental
Incremental Static Regeneration (ISR) is a hot topic in the Jamstack category right now. The ability to perform incremental builds has long been a goal of many frameworks and libraries in order to minimise build times, and deliver more dynamic, larger sites which update more often.
With the right building blocks, and a suitable approach, ISR is attainable already. This talk will explore how you can implement a site with ISR using any site generation tools, and retain a code and hosting architecture which you can understand and reason about. How you don’t need to adopt any single framework to deliver this “as if by magic”. And how you can retain powerful attributes of Jamstack sites such as immutable and atomic deploys, even while using an ISR model.