Static Websites
on AWS
w/ Nuxt
Michael Cole
Freelancer
Poverty Abolitionist
michael@CoalitionToAbolishPoverty.org
Static Website on AWS w/ Nuxt
What is the difference between 'nuxt build'
and 'nuxt generate' ?
What is a web app?
- Browse to URL
- Login
- Do stuff
- Come back later
- Remembers yer stuff
What is a static site?
- Browse to URL
- Click around
- Read stuff
- Written using declarative content (markdown, not html)
Web Tech Tree
GET URL: server process
GET URL: file
Dynamic Page (PHP, Nuxt Universal)
Static Page (jQuery)
Static Page
<html>
<body>
stuff
</body>
</html>
Serve content from files.
Apache, Nginx
Dynamic Page
<html>
<body>
<?php echo "stuff"; ?>
</body>
</html>
Serve content from URL, templates, and cookies.
CGI, PHP, Ruby on Rails, WordPress
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
GET URL: server process
Click 'page': browser
GET URL: file
Click 'page': file
SPA
<html>
<body>
<a ui-sref="stuff">x</a>
</body>
</html>
One HTML for all URLS. Browser routes "pages".
Angular + ui-router, Backbone, Ember, Vue
Static Site
index.html
blog/hello.html
blog/post-more-often.html
about-us/index.html
contact/index.html
Prerender HTML for each URL w/ declarative content.
Jekyll, Wintersmith, Hexo, Github pages, Nuxt
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
GET URL: server process
Click 'page': browser
GET URL: file
Click 'page': file
!!!
Web Tech Tree
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
GET URL: server process
Click 'page': browser
! Not SEO friendly !
GET URL: file
Click 'page': file
! Not an App !
!!!
nuxt static site (pre-rendered)
By default, Vue components produce and manipulate DOM in the browser as output.
However, it is also possible to:
- render the same components into HTML strings on the server (e.g. as files)
- send them directly to the browser
- finally "hydrate" the static markup into a fully interactive app on the client (asyncData)
This is a Vue.js feature:
Web Tech Tree
Statically Generated SPA (Nuxt Static Site)
Progressive Web App (Offline, Nuxt PWA)
GET URL: file
Click page: browser
"Hydrate": REST (asyncData)
Dynamic Page (PHP, Nuxt Universal)
Single Page App (Nuxt SPA)
Static Site Gen (Github pages)
Static Page (jQuery)
nuxt generate
- Transpile declarative content
- into a
Nuxt Universal/Vue SPA
Then:
- pre-render with Vue SSR
- into a
Nuxt Static Site
nuxt build
- Transpile declarative content
- into a Nuxt Universal/Vue SPA
code & content
/layouts
/pages
/assets
/components
/plugins
Nuxt Universal App (Express)
/.nuxt/index.js
Static Site
/index.html
/pages/index.html
/_nuxt/*
nuxt build
(aka Server Rendered Deployment) (aka Nuxt Universal)
// ------------------------- Dev:
nuxt
// or
npm run dev
// ------------------------- Production:
// compile to .nuxt directory
nuxt build
// run web server with Node.js
nuxt start
nuxt build -> .nuxt
GET page: server process
Click page: browser
// ------------------------- Dev:
nuxt || npm run dev
// ------------------------- Production:
// compile static site to dist directory
nuxt generate
// Push files to web server...
nuxt_deploy_or_whatever.sh
nuxt generate
(aka Server Generated Deployment) (pre-rendered) (static site)
nuxt generate -> dist
GET page: file
Click page: browser
"Hydrate": REST (asyncData)
Static Websites
on AWS
w/ Nuxt
nuxt deploy? Nope.
Laptop:
`nuxt generate`
?
Cloud:
?
nuxt deploy? Nope.
Laptop:
`nuxt generate`
Why Cloud?
- More secure. No server to p0wn.
- Faster page loads. CDN
- Free SSL cert.
- Cheaper hosting. $0.84 /month
Why AWS?
I already have an account.
Gulp code from wintersmith project
nuxt on AWS w/ S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
npm run deploy
nuxt generate
deploy.sh
gulp
AWS
Laptop
AWS: S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
Stores files
Static website hosting
http://coalitiontoabolishpoverty.org.s3-website-us-east-1.amazonaws.com
Content Delivery Network
Certificate Manager
"Invalidate" cache to refresh from S3.
https://coalitiontoabolishpoverty.org
nuxt on AWS w/ S3 + Cloudfront
SSL
CDN
Browser
S3 bucket
CloudFront
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
Deploy our stuff:
1. Push files -> S3
2. Invalidate CloudFront to refresh from S3
nuxt on AWS w/ S3 + Cloudfront
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
SSL
CDN
Browser
S3 bucket
CloudFront
Read the Nuxt FAQ
nuxt on AWS w/ S3 + Cloudfront
AWS Console tour
- S3 Bucket
- Static Site (AWS_BUCKET_NAME)
- Bucket Policy (Public)
- IAM User Policy
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- CloudFront
- AWS_CLOUDFRONT (uppercase)
- xxxx.cloudfront.net
- DNS ALIAS or CNAME
SSL
CDN
Browser
S3 bucket
CloudFront
nuxt on AWS w/ S3 + Cloudfront
Setup AWS stuff:
1. S3 bucket
2. CloudFront -> S3
3. DNS -> CloudFront
4. Securely push files -> S3
We need this data:
- AWS_BUCKET_NAME="example.com"
- AWS_CLOUDFRONT="UPPERCASE"
- AWS_ACCESS_KEY_ID="key"
- AWS_SECRET_ACCESS_KEY="secret"
Read the Nuxt FAQ
SSL
CDN
Browser
S3 bucket
CloudFront
nuxt on AWS w/ S3 + Cloudfront
Deploy our stuff:
1. Push files -> S3
2. Invalidate CloudFront to refresh cache
(see new content right away)
gulp - streaming build system w/o a webpack PhD
Problem solved w/ NPM packages:
- gulp
- gulp-awspublish
- gulp-cloudfront-invalidate-aws-publish
- concurrent-transform (parallel uploads)
SSL
CDN
Browser
S3 bucket
CloudFront
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
#!/bin/bash
export AWS_ACCESS_KEY_ID="key"
export AWS_SECRET_ACCESS_KEY="secret"
export AWS_BUCKET_NAME="example.com"
export AWS_CLOUDFRONT="UPPERCASE"
# If nvm (node version manager),
[ -s "$HOME/.nvm/nvm.sh" ] \
&& source "$HOME/.nvm/nvm.sh" \
&& nvm use
# Npm install if not already.
[ ! -d "node_modules" ] && npm install
nuxt generate
gulp deploy
# or configure package.json scripts
npm run generate
npm run deploy
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
$ chmod +x deploy.sh
$ echo "
# Don't commit build files
node_modules
dist
.nuxt
.awspublish
deploy.sh
" >> .gitignore
Deploy our stuff:
1. deploy.sh
2. .gitignore
3. packages!
4. gulpfile.js
5. Deploy already!
$ npm install --save-dev \
gulp \
gulp-awspublish \
gulp-cloudfront-invalidate-aws-publish \
concurrent-transform
$ npm install -g gulp
4. gulpfile.js
var gulp = require('gulp');
var awspublish = require('gulp-awspublish');
var cloudfront = require('gulp-cloudfront-invalidate-aws-publish');
var parallelize = require('concurrent-transform');
// https://docs.aws.amazon.com/cli/latest/userguide/cli-environment.html
var config = {
// Required
params: { Bucket: process.env.AWS_BUCKET_NAME },
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
// Optional
deleteOldVersions: false, // NOT FOR PRODUCTION
distribution: process.env.AWS_CLOUDFRONT, // CloudFront distribution ID
region: process.env.AWS_DEFAULT_REGION,
headers: { /*'Cache-Control': 'max-age=315360000, no-transform, public',*/ },
// Sensible Defaults - gitignore these Files and Dirs
distDir: 'dist',
indexRootPath: true,
cacheFileName: '.awspublish',
concurrentUploads: 10,
wait: true, // wait for CloudFront invalidation to complete (about 30-60 seconds)
}
gulp.task('deploy', function() {
// create a new publisher using S3 options
// http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property
var publisher = awspublish.create(config, config);
// Collect all the files in distDir
var g = gulp.src('./' + config.distDir + '/**');
// Send those files through publish.
g = g.pipe(parallelize(publisher.publish(config.headers), config.concurrentUploads))
// Invalidate distribution cache so new content appears
if (config.distribution) {
console.log('Configured with CloudFront distribution');
g = g.pipe(cloudfront(config));
} else {
console.log('No CloudFront distribution configured - skipping CDN invalidation');
}
// Delete removed files - breaks older versions being viewed
if (config.deleteOldVersions) g = g.pipe(publisher.sync());
// create a local cache of files to speed up consecutive uploads
g = g.pipe(publisher.cache());
// print upload updates to console
g = g.pipe(awspublish.reporter());
return g;
});
$ ./deploy.sh
Found '/home/michael/scm/example.com/www/.nvmrc' with version <8>
Now using node v8.11.2 (npm v5.6.0)
> example.com@1.0.0 generate /home/michael/scm/example.com/www
> nuxt generate
nuxt:generate Generating... +0ms
nuxt:build App root: /home/michael/scm/example.com/www +0ms
nuxt:build Generating /home/michael/scm/example.com/www/.nuxt files... +0ms
nuxt:build Generating files... +36ms
nuxt:build Generating routes... +10ms
nuxt:build Building files... +24ms
████████████████████ 100%
Build completed in 7.009s
DONE Compiled successfully in 7013ms 21:25:22
Hash: 421d017116d2d95dd1e3
Version: webpack 3.12.0
Time: 7013ms
Asset Size Chunks Chunk Names
pages/index.ef923f795c1cecc9a444.js 10.6 kB 0 [emitted] pages/index
layouts/default.87a49937c330bdd31953.js 2.69 kB 1 [emitted] layouts/default
pages/our-values.f60c731d5c3081769fd9.js 3.03 kB 2 [emitted] pages/our-values
pages/join-us.835077c4e6b55ed1bba4.js 1.3 kB 3 [emitted] pages/join-us
pages/how.75f8cb5bc24e38bca3b3.js 2.59 kB 4 [emitted] pages/how
app.6dbffe6ac4383bd30a92.js 202 kB 5 [emitted] app
vendor.134043c361c9ad199c6d.js 6.31 kB 6 [emitted] vendor
manifest.421d017116d2d95dd1e3.js 1.59 kB 7 [emitted] manifest
+ 3 hidden assets
Hash: 9fd206f4b4e571e9571f
Version: webpack 3.12.0
Time: 2239ms
Asset Size Chunks Chunk Names
server-bundle.json 306 kB [emitted]
nuxt: Call generate:distRemoved hooks (1) +0ms
nuxt:generate Destination folder cleaned +10s
nuxt: Call generate:distCopied hooks (1) +8ms
nuxt:generate Static & build files copied +7ms
nuxt:render Rendering url /our-values +0ms
nuxt:render Rendering url /how +67ms
nuxt:render Rendering url /join-us +1ms
nuxt:render Rendering url / +0ms
nuxt: Call generate:page hooks (1) +913ms
nuxt: Call generate:page hooks (1) +205ms
nuxt: Call generate:page hooks (1) +329ms
nuxt: Call generate:page hooks (1) +361ms
nuxt:generate Generate file: /our-values/index.html +2s
nuxt:generate Generate file: /how/index.html +0ms
nuxt:generate Generate file: /join-us/index.html +0ms
nuxt:generate Generate file: /index.html +0ms
nuxt:render Rendering url / +2s
nuxt: Call generate:done hooks (1) +4ms
nuxt:generate HTML Files generated in 11.8s +5ms
nuxt:generate Generate done +0ms
[21:25:27] Using gulpfile ~/scm/example.com/www/gulpfile.js
[21:25:27] Starting 'deploy'...
Configured with CloudFront distribution
[21:25:27] [cache] README.md
[21:25:27] [cache] android-chrome-192x192.png
[21:25:27] [cache] android-chrome-512x512.png
[21:25:27] [cache] apple-touch-icon.png
[21:25:27] [cache] browserconfig.xml
[21:25:27] [cache] favicon-16x16.png
[21:25:27] [cache] favicon-32x32.png
[21:25:27] [cache] favicon.ico
[21:25:27] [cache] favicon.svg
[21:25:27] [cache] logo-branches.svg
[21:25:27] [cache] logo-small.svg
[21:25:27] [cache] logo.svg
[21:25:27] [cache] mstile-150x150.png
[21:25:27] [cache] og-image.jpg
[21:25:27] [cache] safari-pinned-tab.svg
[21:25:27] [cache] site.webmanifest
[21:25:28] [create] _nuxt/manifest.421d017116d2d95dd1e3.js
[21:25:29] [update] 200.html
[21:25:30] [create] videos/flag.jpg
[21:25:30] [create] _nuxt/vendor.134043c361c9ad199c6d.js
[21:25:34] [create] videos/flag.mp4
[21:25:34] [cache] _nuxt/pages/how.75f8cb5bc24e38bca3b3.js
[21:25:34] [cache] _nuxt/pages/join-us.835077c4e6b55ed1bba4.js
[21:25:34] [cache] _nuxt/pages/our-values.f60c731d5c3081769fd9.js
[21:25:36] [update] our-values/index.html
[21:25:36] [create] _nuxt/layouts/default.87a49937c330bdd31953.js
[21:25:36] [create] _nuxt/app.6dbffe6ac4383bd30a92.js
[21:25:37] [create] _nuxt/pages/index.ef923f795c1cecc9a444.js
[21:25:38] [update] join-us/index.html
[21:25:38] [update] how/index.html
[21:25:43] [create] videos/flag.webm
[21:25:43] [update] index.html
[21:25:43] CloudFront invalidation created: I16NXXXXX4JDOA
[21:26:09] Finished 'deploy' after 42 s
5. Deploy Already!
michael@CoalitionToAbolishPoverty.org
Static Websites on AWS w/ Nuxt
By michaelcole
Static Websites on AWS w/ Nuxt
- 1,101