Progressive Bundling Example
Tags: serverless, progressive, bundling, javascript
Published: 2020-04-07 19:29:27

---
title: Progressive Bundling Example
published: true
description: Step through a project that has progressive bundling enabled to see how you might be able to use this approach in your next project. 
tags: serverless, progressive, bundling, JavaScript
---

> This is a follow-up code example for this post introducing Progressive Bundling: [Progressive Bundling Post](https://dev.to/begin/progressive-bundling-1gj2).
##### Check out the live example here: [Live Example](https://art-jyw.begin.app/)

Let's step through a project that has progressive bundling enabled to see how you might be able to use this approach in your next project.

You can follow along at home with this repo: <a href="https://github.com/begin-examples/node-progressive-bundling">begin-examples/node-progressive-bundling</a>

Alternatively you can deploy your own on [Begin](https://begin.com) by clicking this link: <a href="https://begin.com/apps/create?template=https://github.com/begin-examples/node-progressive-bundling" target="_blank">Deploy to Begin</a>

To see the Progressive Bundling pattern in action, open up your browser's development tools to watch the network as the page loads.
 
Watch for a request to be made for `home.js`, and you should see a 302 redirect to a bundled and fingerprinted version of that module.

![302 redirect to bundled module](https://dev-to-uploads.s3.amazonaws.com/i/xihbpyo7brqbcrscboxc.jpg)

#### Project overview

Now that we've seen it in action let's get acquainted with the project. This is a standard Architect serverless project and while you don't need to know Architect to follow along you might want to have <a href="arc.codes" target="_blank" rel="noopener">arc.codes</a> open in case any questions come up.

Let's start at the beginning and look at how to configure the project's infrastructure.
#### `.arc`
The project's `.arc` file is how we declare this app's infrastructure as code. We declare all of the app's HTTP Functions under the `@http` pragma.

```sh
@app
pb-example

@static
folder public

@http
get /
get /about
get /modules/:type/:module
get /cache
post /cache

@tables
data
  scopeID *String
  dataID **String
  ttl TTL

```

###### folder structure
```sh
├── src/
│   └── http/
│   │    └── get-index/
│   └── views/
│        └── modules/
│   └── shared/
└── .arc
```

##### `src/http`
This folder is where all the HTTP function's source code lives. Architect maps these HTTP Functions to the routes declared under the `@http` pragma in the app's `.arc` file.

> By convention, `get-index/` is mapped to the route '/' and serves `https://yoursite/`. Similarly, `get-about` is assigned to `/about` and serves `https://yoursite/about`. A `post-cache` route would accept posts from forms having the action attribute set to `action="/cache"`.

##### `src/shared`
This folder is where you put code you want to share between functions. Any code placed inside `src/shared` will be available to require from `@architect/shared`.

##### `src/views`
This folder is where all of your shared view code lives. These files are copied to all the `GET` routes by default allowing you to share view code throughout your app. Any code placed inside `src/views` will be available to require from `@architect/views`.


#### Implementation
This example app implements the Progressive Bundling pattern with a few discrete steps.

- **cache lookup:**
First, we check to see if the file already exists in the cache. If it does, we return it.
- **bundle:**
Then if the requested file isn't already in the cache, we bundle it.
- **fingerprint:**
Next, we generate a fingerprint id and append it to the file name.
- **cache**
Then this new file is cached for subsequent requests.
- **redirect:**
Finally, we redirect to the newly cached file.

> It is possible to create a fingerprint by using a hash of the bundled file's contents. Appending a fingerprint to the file name ensures that the file name is updated when the content of the bundle changes. Adding a fingerprint to the file name avoids getting an outdated file due to a cached version in the end user's browser or network.

Now that we know the steps let's follow a request for a module through to the bundled response.

The route we are going to focus on first is `get /modules/:type/:module`.  This route passes a module type and module name to our HTTP Function as parameters on the request.

> The folder for this route is generated by Architect the first time you start the sandbox server. The folder for this route replaces the colons ":" that defines a route to a system-friendly three zeros "000". This means that the route `get /modules/:type/:module` will generate the folder `src/http/get-modules-000type-000module`.

[`src/http/get-modules-000type-000module`](https://github.com/begin-examples/node-progressive-bundling/blob/master/src/http/get-modules-000type-000module/index.js)
```js
// Check to see if file is in cache
let file = await read({ name })
// Bundle the file if it is not found in the cache
if (!file) {
  file = await bundle({ name })
}
// Redirect to the file
return redirect(`/_static/${ file }`)
```
The above code is where all the action is in this file. This code first looks to see if there is a cached version to send; if not, it bundles the requested file then redirects the request to the bundled version.

Let's look at how we have implemented the cache.

[`src/shared/cache-read.js`](https://github.com/begin-examples/node-progressive-bundling/blob/master/src/shared/cache-read.js)
```js
const data = require('@begin/data')
// check the cache manifest
let cache = await data.get({
  table: 'module-cache',
  key: name
})
```
In the previous code block, we look for the module by name in a table called `module-cache` then return the file if found. In this example, we use [Begin data](https://github.com/smallwins/begin-data) for simplicity, but any data store would work.

Next is the code responsible for bundling the file.

[`src/http/get-modules-000type-000module/bundle.js`](https://github.com/begin-examples/node-progressive-bundling/tree/master/src/http/get-modules-000type-000module/bundle.js)
```js
// Get the path to this module on disk
let input = path({ name })
// Pass the file path to rollup
let bundle = await rollup.rollup({ input })
// Bundle together modules
let bundled = await bundle.generate({ format: 'esm' })
```
Above we look up the file and then pass it to rollup to bundle. Rollup was used in this example, but you could substitute the bundler of your choice.

One last stop at the redirect code before sending the response.

[`src/http/get-modules-000type-000module/302.js`](https://github.com/begin-examples/node-progressive-bundling/blob/master/src/http/get-modules-000type-000module/302.js)
```js
module.exports = function redirect(location) {
  return {
    statusCode: 302,
    headers: { location }
  }
}
```
Pretty straight forward, but it's useful to see how to send any type of reponse with a status code.

#### Wrapping up
We stepped through a project implementing this pattern and looked at all the places this pattern can be customized to fit your own needs. This example illustrates how Progressive Bundling can be implemented but is by no means the end. You can apply this pattern in your way and with other outputs. We can't wait to see what you make!