Modern Web Development on the JAMstack
Kindle Highlights
Figure 3-1. Legacy web stack versus JAMstack
is disabled, by setting an action on the form that wraps
Consider products like Wufoo, Formkeep, Formcarry, or even Google Forms
He stands behind services like headlesscms.org, testmysite.io, and jamstack.org, and is one of the originators of the JAMstack term.
User sessions are saved in localStorage, so the UI can access the user payload and use it to show the user’s name where it makes sense
JavaScript in the browser as the runtime Reusable HTTP APIs rather than app-specific databases Prebuilt markup as the delivery mechanism
The ability to serve the frontend of a site from CDN infrastructure optimized for static assets should be viewed as a superpower rather than a limitation.
The Auth0 team that pioneered this standard maintains a useful resource where you can find more in-depth information and debugging tools for inspecting and verifying tokens.
The product catalog is built out by Hugo during the build step, and all products are managed with Netlify CMS. A simplified version of an ebook file stored in the Git repository looks like this:
When the same application that builds the page views or lets authors manage content also needs to be scaled to handle traffic spikes, it’s not possible to decouple these facilities in order to scale and protect each piece of the infrastructure according to its needs.
But with no servers to store user sessions, how do we connect all of these discrete API calls? How do we handle authentication and identity? To power user accounts, a JAMstack application will often create a secure ID token stored in the browser. (This type of token is called a JavaScript Web Token, or JWT.)
Modern web APIs were officially born with Roy Fielding’s dissertation, “Architectural Styles and the Design of Network-Based Software Architectures,” in 2000. It is a seminal paper that defined Representational State Transfer (REST) as a scalable and discoverable architecture for services exposed through HTTP.
Many of the APIs used are third-party services. Stripe, for example, is a popular service for processing payments. Algolia is a popular API for powering search. (Your authors have a deep love for almost all technology, but would never go back to manually handling payments or running Apache Solr search clusters.)
This means that we’re seeing a much broader ecosystem of ready-made APIs for authentication, comments, ecommerce, search, image resizing, and so on. We can use third-party tools to outsource complexity and rest easy in the knowledge that those providers have highly specialized teams focusing exclusively on their problem space.
The source for the site is a hosted repository that stores content and code together as editable files. Whenever a change made, a build process is triggered that prerenders the site, creating final HTML from templates, content, and data. The prepared, rendered assets are published globally on a CDN, putting them as close to end users as physically possible.
For the majority of content-driven sites, as long as a deploy can be scheduled to go live at a certain time, being able to run a build cycle in less than 10 minutes is likely not of real importance. Unless your site is truly gigantic (pages counted in millions rather than thousands), a JAMstack approach might be a more viable option than you would have once thought.
This extracts the metadata related to the job posting from the order object, formats it into markdown with YAML frontmatter, and then uses GitHub’s content API to push the new job posting to the main Smashing Magazine repository. This in turn triggers a new build, where Hugo will build out the updated version of the job board and Netlify will publish the new version.
The approach to designing a system that allows for hundreds of millions of read operations is very different to that of a system that allows a similar number of write operations. So, is it wise to combine these capabilities into the same infrastructure and demand that the risk profile of one feature influence that of another? And are those features likely to encounter similar traffic levels?
In this case, the behavior of the identity service is a little different. When a Lambda function is deployed via Netlify on a project that has an identity service, the function will have privileged access to the identity service. This is a common pattern, and you can similarly set up Lambda function using Amazon CloudFront and Amazon API Gateway together with AWS’s identity service (Cognito) to have privileged access.
After years of XHR going more or less unnoticed, Jesse James Garrett from the agency Adaptive Path wrote an article in February 2005 that would change the web: “Ajax: A New Approach to Web Applications.” In the article, Jesse coined the term “Ajax” and gave web developers a way to communicate about the new patterns that emerged after XHR was used to fetch data from servers directly from JavaScript — similar to how the JAMstack terminology now lets us talk about a new architecture under the banner of a unifying nomenclature.
CDNs are in the business of servicing high volumes of traffic on our behalf. They are designed to offer redundancy, invisible and automatic scaling, load balancing, caching, and more. The emergence of specialist CDN products and services brings opportunities to outsource all of this complexity to companies whose core business is providing this type of resilience and scale in ways that do not require you to manage the underlying infrastructure. The more of our site’s assets we can get onto a CDN, the more resilient it will be.
In the example flow shown in Figure 4-3, notice how content is managed in a hosted CMS service called Contentful. Store owners and editors use the Contentful interface to manage store listings and upload photos. Contentful is termed a “headless” CMS because its only concern is storing the content, not the display of it. Figure 4-3. Sample JAMstack commerce setup Adding a new item in Contentful triggers a new build of the website using Netlify’s build automation. During the build step, Netlify fetches the most recent store data from Contentful by calling the API.
Content Management There are a number of different approaches to content management on the JAMstack, but most involve a headless CMS. Traditionally, a CMS like Wordpress is both the tool used by admins to manage content and the tool used for building out the HTML pages from that content. A headless CMS separates those two concerns by focusing on only the content management, and delegating the task of using that content to build out a presentation layer to an entirely separate toolset. You can find a listing of headless CMS offerings at https://headlesscms.org/. They’re divided into two main groups:
Fewer moving parts at runtime means fewer things that could fail at a critical moment. And the more distance we can put between our users and the complexity inherent in our systems, the greater our confidence that they will experience what we intended. Facing that complexity at build time, in an environment that will not affect the users, allows us to expose and resolve any problems that might arise safely and without a negative impact. The only reason to run server-side code at request time ought to be that we absolutely cannot avoid it. The more we can liberate ourselves from this, the better the experience will be for our users,
With traditional architectures, in which page requests might require activity at every level of the stack, that capacity will need to extend through each tier, often resulting in multiple, highly specified servers for databases, application servers, caching servers, load balancers, message queues, and more. Each of these pieces of infrastructure has an associated financial cost. Previously, that might have included the cost of the physical machines, but today these machines are often virtualized. Whether physical or virtual, the financial costs of these pieces of infrastructure can mount up, with software licenses, machine costs, labor, and so on.
Most static site generators have the capability of producing different representations of the content of a site. We can use this to our advantage by generating a search index of the site at compilation time, which then can be queried using JavaScript directly in the browser. The result can be a lightning-fast live search that responds as you type your search queries. For more sophisticated search facilities, services like Algolia can provide extensive content modeling and be customized to include data insights (with knowledge of your site’s taxonomy), provide fuzzy matching, and more. From small sites to large enterprises, Algolia has identified the value in providing powerful search and bringing it to all types of technology stacks, including the JAMstack.
Stateless authentication takes a different approach. Instead of passing a session ID when we call a microservice, it passes a representation of the user. This could look something like the following: { “sub”: “1234”, “email”: “matt@netlify.com”, “user_metadata”: { “name”: “Matt Biilmann” }, “app_metadata”: { “roles”: [“admin”, “cms”], “subscription”: { “id”: “4321”, “plan”: “smashing” } } } Now each microservice can look at this payload and act accordingly. If an action requires an admin role, the service can check whether app_metadata.roles in the user payload includes an admin role. If the service needs to send an order confirmation to the user, it can rely on the email property. This means that each microservice can look for the payload information it needs without having any concept of a central database with user information and without caring about which system was used to authenticate the user. However, we need a way to establish trust and ensure that the payload of the user really represents a real user in the system. JSON Web Token (JWT) is a standard for passing along this kind of user payload while making it possible to verify that the payload is legitimate. A JWT is simply a string of the form header.payload.signature with each part of bas64 encoded to make it safe to pass around in HTTP headers or URL query parameters. The header and payload are JSON objects, and the signature is a cryptographic signature of the header and the payload.
An entire community grew up around the first mature generation of static site generators like Jekyll, Middleman, Metalsmith, Cactus, and Roots. Jekyll found fame as the static site generator supported natively by GitHub Pages, and others were developed in-house by agencies or startups that made them a core part of their website stack. The tools brought back a feeling of simplicity and control from the complex database-based Content Management Systems (CMSs) that they replaced. At the same time, single-page app frameworks like Vue and React started a process of decoupling the frontend from the backend, introducing build tools like Grunt, Gulp, and later, Webpack and Babel, which all propagated the idea of a self-standing frontend with its own build pipeline and deployment process. This brought a proper software architecture to the frontend workflow and empowered frontend developers to iterate much faster on the user interface (UI) and interaction layer. When these approaches to building websites and applications intersected, a new API ecosystem began to emerge. Tools like Stripe, Disqus, and Algolia made payments, comments, and search available directly from the frontend via API-driven services. A large set of terms for the different pieces of these new software architectures began to appear: progressive web apps, static sites, frontend build pipelines, single-page applications, decoupled web projects, and serverless functions. However, none of them really captured the move to a new architecture that encompassed content-driven websites, web applications, and all the interesting hybrids between the two. The JAMstack put a name to all of it.