/fab

πŸ’Ž FAB project specification & monorepo

Primary LanguageJavaScriptMIT LicenseMIT

name route
Home
/

fab logo wide
Build once, run everywhere

FAB Specification

πŸ’Ž FABs are a compile target for frontend applications.

They unify static sites, single page applications (SPAs) as well as server-rendered JavaScript UIs in one bundle format, allowing instant deployment to a wide range of hosting platforms.

https://github.com/fab-spec/fab

Framework adapters

Note: for now, you probably want to get started with @fab/static. It's a zero-config compiler for static sites with hooks to add as much server-side logic as most apps need.

Working with one of these projects? Read the following

Thinking of writing your own adapter? Head to @fab/compile to understand the low-level compilation API. Then get in touch, we're happy to help!

Deployment adapters

πŸ’Ž FABs are portable. That is, once they're compiled, they can be deployed to any number of

Thinking of writing your own deployment adapter? Read on to understand the runtime requirements of FABs, then check @fab/serve for some sample code.

The FAB format

Note: This is work-in-progress, under active development by @glenmaddern.

What is a Frontend Application?

The term Frontend Application encompasses a wide range of modern web projects, from purely static sites with no client-side JS, to entirely client-rendered apps hitting an API, or those with a significant server-side-rendering component. But they are defined in opposition to a more traditional "backend" application, which may emit HTML across the wire as well, but usually has a persistent server, with direct connections to databases and a local filesystem.

This is synonymous with some of the more common web app development methodologies in the React/Angular/Vue/Ember ecosystemsβ€”a self-contained single-page-app, potentially pre-rendered or server-rendered, talking to a separate backend app or collection of services via HTTP.

Why a new bundle format?

The Frontend Application Bundle is designed to fill a gap between existing options for frontend application deployment and hosting environments. Usually, you have a choice between a static site host, which prevents you from having any active server-side components, or a more traditional web app host designed for hosting backends.

And while deploying frontend apps to backend-centric hosts works reasonably well, it misses a crucial aspect of frontend web developmentβ€”iteration speed. UI development benefits greatly from rapid prototyping and feedback, and since frontend apps, having no direct dependencies on databases or filesystems, can be cloned & deployed freely, backend-centric workflows can feel overly constrained.

As such, static site hosting has grown in popularity among the frontend application community, and static site generators along with them. But there are many reasons why it's preferable or even essential to include a server-side component in your application, which these projects can't take advantage of without fundamentally changing how they build & deliver their app.

Frontend Application Bundles are the container format that work equally well for fully-static through to full server-rendered frontend apps, making your choice of technology independent from your choice of hosting.

fab.zip specification

The FAB file is a zip* file containing two parts:

fab.zip
  β”œβ”€β”€ server.js   (server entry point)
  └── _assets     (directory of assets for this release)

All FAB tooling uses deterministic-zip to create this file, which means that two FABs with identical contents will themselves be identical.

server.js

A V8:Isolate-compatible single-file build of your server-side logic.

Exposes two entry points:

const getProdSettings = () => {
  return {
    // All production settings are bundled into the FAB here,
    // ensuring predictable behaviour regardless of host.
    // These are returned as a separate entry point, however,
    // so that any encrypted variables can be decrypted
    // by the host before being passed through to the render.
  }
}

const render = async (request, settings) => {
  // request: a fetch.Request object
  // settings: your production settings plus any
  //           environment-specific overrides
  
  const { body, statusCode, headers } = 
                  await myApp.render(request, settings)
  
  // return a fetch.Response object with the full data.
  // Streaming responses not yet fully supported, but will be.
  return new Response(body, { statusCode, headers })
}

module.exports = { render, getProdSettings }

_assets directory

Extracted from the FAB and hosted separately on a static file server like S3 at deploy time, then routed there by a CDN or load balancer using the URL path /_assets/*. This happens before your request even reaches your FAB, which means that the _assets directory cannot be changed. More importantly, and assets are recommended to be served with cache-control: immutable headers. As such, files in this directory must be fingerprinted so they do not clash from release to release.

Since there can be no static assets outside the /_assets directory, and all assets must be fingerprinted, we provide @fab/compile which takes a more user-friendly format and generates a spec-compliant FAB.

Please star this project to follow along!


@glenmaddern.