JAM Stack: JavaScript, API, Markup. This is an architecture approach which seeks to reduce the coupling between frontend and backend codebases, facilitate horizontal scaling (scaling by running / distributing the codebase across multiple web servers), and improve performance by pre-rendering content. This architecture is not technology-specific; it can be implemented using Javascript + JQuery + PHP, Angular + Node JS, React + Java, or any other technology combinations which are suited for web development. For the purposes of this article, we will consider two types of implementations, pure and impure, how this relates to the Byte This! website, and how this concept can be applied elsewhere.
- Pure JAM Stack: an implementation which is true to the original concepts of JAM stack: no server side rendering, etc.
- Impure JAM Stack: an implementation which violates one or more of the JAM stack requirements but still honors its fundamental concepts.
This website is an example of an impure approach, primarily because we use Angular SSR to dynamically render the website's metadata on-demand. The sections below will provide more details.
Pure JAM Stack
The official JAM Stack websites provides full details on the concepts behind JAM stack, the benefits of the approach, and some of its best practices. The following steps outline a basic summary of how to implement a JAM stack based website:
- Create some frontend application whose scripts and resources do not require server side interpolation (for example: no "$var" type interpolation on the HTML or JavaScript documents on the server side).
- Implement the backend, or use external services, for any service and database needs. The frontend will interact via an API layer provided by the backend. The official documentation recommends a micro service approach.
- Generate the static files for the frontend. The steps to do this will vary based on the type of framework you are using. Angular or React, for example, will assemble everything for you. If you are using plain JavaScript, your source may already serve as the needed output.
- Upload your static files to some hosting service. The official documentation recommends uploading to a CDN (content distribution network).
- Deploy the backend services. If you are consuming external services instead of using your own backend, this step is not needed.
If the steps above are followed correctly, your project will look something like this:
- The frontend and backend source code will be totally decoupled.
- The frontend files will be static files; the files can be sent to the browser as-is.
- The frontend application will make use of the backend's (or external service's) API layer to handle user authentication, data retrieval, and other requirements.
Some of the benefits of taking this approach include:
- Faster Initial Page Load: the client receives the template and script files directly from a CDN; the lack of server side processing reduces the time it takes for the client to get the initial files. The client can then quickly display some initial welcome screen, loading bar, or similar, while the page fetches what it needs from the API.
- Easier Scaling: when a project starts gaining traction, one server might not provide enough performance and resources. The decoupling of the frontend and backend + the static file generation simplifies the process of scaling. Frontend static files can be served from multiple locations easily, since they are static files only (no processing or rendering).
- Low Coupling of Code: the frontend and backend will be lowly coupled; the frontend does not need the backend to provide code, values, etc. before delivery. If done properly, the entire backend or frontend codebases could be entirely reimplemented without affecting the other. From a code implementation and maintenance perspective, this is a very important point and one of the best benefits the JAM stack architecture provides.
Impure JAM Stack
An "impure" implementation (a term which I've coined for this article) is an implementation which one might describe as "similar to JAM Stack" or "inspired by JAM stack concepts" but would not describe as a pure JAM Stack implementation. The concept of an "Impure" JAM Stack implementation is a bit flexible; certain implementation details may or may not make the implementation "impure". Arguably, the following will make an implementation impure:
- Non Micro Service Based Backend: a backend which is monolithic or otherwise not based on the micro service concept.
- Template Processing On-Demand: a backend which does some processing on the template files before delivering them to the client. In this case, the template files are not static.
- Non CDN-Based Serving: static files are not served from a CDN.
- Improper Deployment Process: any deployment process which does not follow JAM stack best practices.
The more violations occur, the less you can call something "JAM Stack based".
Backend as a Service
A concept closely related to JAM Stack is backend as a service. This is a model where a developer outsources all of the backend requirements to external services, such as database reading and writing, user authentication, sending emails and push notifications, and domain related orchestration logic. A developer can leverage one or more external service providers to leverage these services without needing to write any backend code themselves. This type of approach works perfectly with the JAM stack architecture (though is not required by it); it is conceptually similar to a micro service based backend approach. Outsourcing all backend related concerns has its advantages and disadvantages, this apporach should be considered carefully and against alternatives, such as implementing your own backend entirely or implementing your own backend while consuming some external services for specific concerns.
JAM Stack and Byte This!
This website was built with JAM Stack concepts in mind, but falls within the "impure" classification. We are using Angular SSR (server side rendering) to insert HTML metadata tags before delivering each template document to the client. We've taken this approach to facilitate dynamic content rendering; our store data is consumed dynamically from a 3rd party service. It is possible to change our approach to generate static files based on this dynamic data + regenerate and redeploy on 3rd party data updates, but we have decided not to take this approach at this time. Additionally, our backend is not implemented based on micro services. However, our frontend and backend codebases are entirely de-coupled. The Angular framework encapsulates all of the backend rendering; from the developer's perspective, there is no coupling or interaction. The frontend and backend codebases can be deployed independently.