Host Nuxt SSR app on AWS using Serverless
Tags: serverless, vue, javascript, node
Published: 2021-02-03 04:16:02

I struggled through being able to get my portfolio site online because I haven’t found any suitable documentation online for this process. There are a lot of random articles online that say this is how you can host Nuxt SSR on AWS but a lot of them either end up mentioning using the Nuxt generate option (not SSR) or can be a bit complex to actually set up. I’ve never made a post about anything but wanted to in order to help anyone else out that’s struggling with the same issue.

The great thing about using Serverless for deploying a Nuxt site is that if you have other AWS services you want to integrate it’s as simple as adding some **iamRoleStatements**. Then you can access the data from those services through an Expressjs middleware and retrieve them from the **res** of the Nuxt **context**.

### Prerequisites:
* Nuxt
 * Basic understanding of Nuxt and how it’s SSR works
 * If you don’t then I suggest you look over their docs and examples
  * <https://nuxtjs.org/docs/2.x/get-started/installation>
  * <https://nuxtjs.org/examples>
* Serverless
 * Basic understanding of the Serverless Framework (AWS as a provider)
 * If not you can look over these docs 
  * <https://www.serverless.com/framework/docs/providers/aws/guide/intro/>
 * Have an AWS IAM User configured through aws cli
 * If not the Serverless docs get into that here 
  * <https://www.serverless.com/framework/docs/providers/aws/guide/credentials#create-an-iam-user-and-access-key>
* Expressjs
 * Basic understanding of Expressjs
 * If not then you should review their docs 
  * <https://expressjs.com/en/starter/installing.html>
* AWS
 * Either already have or be able to create a domain name through AWS Route 53 and ssl certs through AWS Certificate Manager
  * <https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/registrar.html>
  * <https://docs.aws.amazon.com/acm/latest/userguide/gs.html>


### Step 1: Create demo Nuxt app
To create the demo project run
```sh
$ npm init nuxt-app nuxt-serverless
```
in your terminal. For the Rendering mode prompt select **Universal (SSR / SSG)**. For the Deployment target select **Server (Node.js hosting)**. For all other prompts select which options you want for your project. Next to install the necessary dependencies `cd` into your project folder and run
```sh
$ npm i aws-serverless-express express serverless-http
```
To install the necessary devDependencies run
```sh
$ npm i -D serverless serverless-offline
```
Once the project is created open it in your preferred text editor. You’ll need to create an **app.js** file in order to programmatically serve Nuxt info using Expressjs. Copy and paste in this code:
```javascript
// app.js
'use strict'

const { createServer, proxy } = require('aws-serverless-express')
const express = require('express')
const { Nuxt } = require('nuxt')
const config = require('./nuxt.config.js')

const app = express()
const nuxt = new Nuxt({
  ...config,
  dev: false,
  _start: true
})

app.use(async (req, res) => {
  if (nuxt.ready) {
    await nuxt.ready()
  }
  nuxt.render(req, res)
})

const server = createServer(app, void 0, [
  'application/javascript',
  'application/json',
  'application/manifest+json',
  'application/octet-stream',
  'application/xml',
  'font/eot',
  'font/opentype',
  'font/otf',
  'image/gif',
  'image/jpeg',
  'image/png',
  'image/svg+xml',
  'image/x-icon', // for favicon
  'text/comma-separated-values',
  'text/css',
  'text/html',
  'text/javascript',
  'text/plain',
  'text/text',
  'text/xml',
  'application/rss+xml',
  'application/atom+xml'
])

module.exports.handler = (event, ctx) => {
  proxy(server, event, ctx)
}

```

You’ll need to create a **serverless.yml** file in order for serverless to package your code into a Lambda. Copy and paste in this code:
```yaml
# serverless.yml
service: nuxt-serverless

provider:
  name: aws
  runtime: nodejs12.x
  stage: ${opt:stage, 'dev'}
  region: us-west-2
  apiGateway:
    binaryMediaTypes:
      - '*/*'
  environment:
    # stops telemetry output from nuxt
    NUXT_TELEMETRY_DISABLED: 1

functions:
  nuxt:
    timeout: 30
    handler: app.handler
    events:
      - http: ANY /
      - http: ANY /{proxy+}

package:
  include:
    # ensures that the build output gets packaged
    - .nuxt/**

plugins:
  - serverless-offline

```

### Step 2: Test run Serverless-Offline
To make sure there are no issues once deployed you’ll want to run:
```sh
$ npm run build && sls offline --stage dev --noTimeout --noPrependStageInUrl
```
While it’s running go to <http://localhost:3000/> and you’ll see your Nuxt app working through Serverless! If there are any issues you should try to google the error outputs and try to determine what’s going on before deploying. You can always reach out to me and I can try my best.

### Step 3: Deploy that sucker
Go ahead and run
```sh
$ sls deploy --stage dev
```
Whatever URL it spits out won’t work due to the staging path Serverless adds but you can go to the URL to see some content load, it’ll just look kind of weird. A custom domain name is required for it to work correctly.

### Step 4: Domain mapping
Sign in to your **AWS console**. If you don’t already have a domain name (Route 53) and ssl certs (Certificate Manager) you’ll want to look through the AWS docs and register/create both of these. If you already have both then in the search bar type in **API Gateway** and select it. In your left navigation bar click on **Custom domain names** and click on **Create**. Type in the domain you’ve registered through Route 53. For the Endpoint type select **Edge-optimized (supports only REST APIs)**. For the ACM certificate select the certificate that was created in Certificate Manager to work with your domain name. Click **Create domain name** and then select **API Mappings**. Click on **Configure API mappings** and click on **Add new mapping**. Set the API you created and the stage it was deployed to (dev in this setup) and click **Save**.

### Step 5: Magic
Now go to your domain name and see the magic 🎉