One App is battle tested as it has been in use in production within American Express with our most highly trafficked customer facing applications since 2016.
It was built with The Twelve-Factor App principles in mind and as such is highly flexible and independent of its runtime.
One App can be started via docker or if built from source by running node lib/server/index.js
on your server.
Contents
- Building And Deploying Holocron Modules
- Building and Deploying a Holocron Module Map
- Building One App
- Configuring One App
- Deploying to AWS Elastic Beanstalk
Holocron Modules are React components that are bundled by
one-app-bundler
and are the pieces that make up a One App application instance.
Running one-app-bundler
on a module builds and packages the module's source code into static assets
that can to be deployed to a CDN. It expects ./src/index.js
to export a React component and
creates a build
directory as its output.
Input:
root
|── src
| └── index.js
└── package.json
Output:
build
└── 1.0.0
├── <moduleName>.browser.js
├── <moduleName>.legacy.browser.js
├── <moduleName>.node.js
└── en-us
├── <moduleName>.json
├── integration.json
└── qa.json
en-us
contains the language packs used for internationalization for the en-US
locale. As this is
an example only en-us
is shown but in your module you may have as many or few locales as needed.
<moduleName>.browser.js
is the modern browser bundle.
<moduleName>.legacy.browser.js
is the legacy browser bundle.
<moduleName>.node.js
is the server bundle.
These bundles are used in One App depending on the environment your module is running in. For example
if One App detects that your module's bundle is being requested by an older browser then
the <moduleName>.legacy.browser.js
bundle will be served which includes polyfills and the needed
transpilation for that browser. Otherwise it will serve the potentially leaner <moduleName>.browser.js
or the <moduleName>.node.js
bundle when running on the server.
Note that if you are making use of code splitting via dynamic imports in your module there may be more chunks in your build
directory.
One App has no opinion on where module bundles are deployed to, the only thing to keep in mind is
that all the assets for a given module must be kept together when deployed, i.e. the file structure
generated by one-app-bundler
must not be modified. This is because the bundles rely on this
structure for knowing where to look for the lang packs. To see examples of how to deploy modules to certain hosting platforms, take a look at our documentation on publishing modules.
In order for One App to use a module two things need to happen:
- The module's static assets must be deployed to a static server.
- A Holocron module map must be created / updated with the module's metadata and hosted on a static server.
A Holocron module map is what tells One App what Modules it should load and where to find them.
It is what allows for Holocron modules to be loaded and used by One App without requiring a One App
deployment or restart. When One App is started on a server it reads from the HOLOCRON_MODULE_MAP_URL
to find out where it should fetch the module map from and then periodically polls this module
map and adds / removes / updates its internal module registry to match any changes to it.
Ultimately a Holocron Module Map is a JSON object of the following shape:
{
"clientCacheRevision": "123",
"modules": {
"<moduleName>": {
"browser": {
"integrity": "sha256-ws6s6vTApdkif2pOfsYOGwdfE9LurZ7Bwq4Olvomrf8= sha384-CLKgejOPhJjRFoUKxLRGeuH09z376SvuTfnWw8IhnShureZQmhzf+GoWGQeA++WU",
"url": "https://example.com/modules/<moduleName>/0.0.0/<moduleName>.browser.js"
},
"legacyBrowser": {
"integrity": "sha256-0wTIJNLsNA9kxoiTPpH0xcseRA+2MezF1r0cdhxx1X0= sha384-jrl8W8VHVqk42r//1LDOYgXG8KIIeBrYMRsEj8bXBEUBNq1X+PUr4XtqGubeoJ36",
"url": "https://example.com/modules/<moduleName>/0.0.0/<moduleName>.legacy.browser.js"
},
"node": {
"integrity": "sha256-LqwNreqEhpaXBRSmhW8/L1MpxcyBsoMwC4IKj8MSFTE= sha384-QLDAyAeq11y9llJhMXd36WwiGg49uJX23EtgaKsCVV83fUJ0rLrswb8V9IoeRIB2",
"url": "https://example.com/modules/<moduleName>/0.0.0/<moduleName>.node.js"
}
}
}
}
The clientCacheRevision
property is used to cache bust all module bundles. This works because its value is appended
to each module asset request from One App.
The modules
property contains module objects containing the URL and
subresource integrity
information for each of the module's assets. There are three assets created by
one-app-bundler
for each module that need to be referenced here for One App to use, browser
,
legacyBrowser
, and node
. The value for the
integrity
property
is generated by one-app-bundler
and can be found in the bundle.integrity.manifest.json
file
within the module's root directory after running one-app-bundler
on it.
Your CI / CD process can generate the module map and update it as modules are added / updated / removed and then publish the file to a static server. Just like with module assets One App does not have an opinion of where the module map is published, it only cares that the module map is accessible shaped correctly.
For a complete example on how to deploy your modules and how to update your module map, please visit the Publishing Modules Recipe.
You can build the One App Docker image and run it in your cloud / data center of choice:
git clone https://github.com/americanexpress/one-app.git
cd one-app
docker build . --build-arg VERSION=$(cat .nvmrc)
Or you can build from source which creates your server side assets at ./lib
and your client
side assets to be deployed to a CDN at ./build
.
git clone https://github.com/americanexpress/one-app.git
cd one-app
NODE_ENV=development npm ci
NODE_ENV=production npm run build
One App is configured via environment variables. There are a few environment variables that are required for One App to start up including the one used to let One App know where to fetch a module map from as described above.
In order for One App to start after it has been deployed, the following environment variables must be set before attempting to deploy / start the One App container:
HOLOCRON_MODULE_MAP_URL
ONE_CLIENT_CDN_URL
ONE_CLIENT_CSP_REPORTING_URL
ONE_CLIENT_REPORTING_URL
ONE_CLIENT_ROOT_MODULE_NAME
Example:
HOLOCRON_MODULE_MAP_URL="https://example-module-map.vercel.app/"
ONE_CLIENT_CDN_URL="https://www.my-app-domain.com/_/static/"
ONE_CLIENT_CSP_REPORTING_URL="https://www.my-app-domain.com/_/report/security/csp-violation"
ONE_CLIENT_REPORTING_URL="https://www.my-app-domain.com/_/report/errors"
ONE_CLIENT_ROOT_MODULE_NAME="my-root-module-name"
Prerequisites:
- AWS account
- A valid module map with at least the root module published and deployed. You can follow this guide to create your module map and deploy your modules.
You can deploy the published One App production image from Docker Hub to Elastic Beanstalk by following these steps:
- Go to the AWS console, select Elastic Beanstalk and click on "Create Application".
- Give your application a name and select "Docker" as the platform.
- Select "Sample Application" and click "Create Application".
- Before we can upload and install the One App image to our new environment, we need to add the Minimum Required Environment Variables. Click on the "Configuration" of your new environment, under the "Software" category click "Edit". Enter all the required environment variables under the "Environment Properties" section.
Important: Ensure that you have entered a valid
HOLOCRON_MODULE_MAP_URL
that is publicly accessible and that at least the root module specified inONE_CLIENT_ROOT_MODULE_NAME
is published; failure to do so, will result in One App crashing and unable to start.
- Click on "Upload and deploy". You will need to upload a
Dockerrun.aws.json
file that contains the following:
{
"AWSEBDockerrunVersion": "1",
"Image": {
"Name": "oneamex/one-app:latest"
},
"Ports": [
{
"ContainerPort": "3000"
}
]
}
Note: You can select the desired version of One App deployed to Docker Hub instead of latest by changing the tag in the image name.
After the deployment is complete, you can navigate to your application by clicking on "Go to environment". If the application health displays an error, One App might be failing to start due to a missing environment variable or missing configuration. You can request the logs and the console should display the reason why One App is crashing.
More information on how to deploy Docker containers on AWS Elastic Beanstalk can be found on the official AWS Documentation.