Universal Apps on Serverless ? Yes.
Tags: javascript, serverless, aws, vue
Published: 2020-07-08 12:55:54

## Introduction

The serverless architecture might be one of the most trending tech paradigms nowadays.
It has been popularized by AWS with its “AWS Lambda platform” and powers millions of APIs worldwide.

We have been using it at [Monisnap](https://www.monisnap.com/fr) for the past year on our micro-services architecture and it has been a blast !
Now, imagine if you could also power your front end apps ( mostly Universal Apps ) with a serverless architecture ?
Sounds great, right ?

## Why serverless ?

You might ask, “Why would I want to do that ? I could just use a static assets provider like Netlify or a good ol’ server running on a vm on the cloud ! ”.

Yes you could but there are a few drawbacks :

- A static assets provider is very useful when you have only a few pages to serve ( or do not care about SEO and just use SPAs ) but imagine if you have millions of generated pages, it is just not imaginable to generate all of them before deployment.
- Running on a server works and has been proven to be quite effective in the past ( and still is to some extent ) but we can do differently now and particularly way cheaper :).

The serverless paradigm gives us a lot of pros but also cons. I’m not gonna get into details about them as it is not the subject of this article but here is a [pretty good overview by Cloudflare](https://www.cloudflare.com/learning/serverless/why-use-serverless/).

You can use any Front-end framework ( or an homemade one ) as long as it allows some kind of programmatic function to be plugged into a node framework / server ( e.g Express ).

You also need your serverless provider to support returning binary types ( as we will be returning html, js , css, etc.. files.

At Monisnap we use [NuxtJS](https://nuxtjs.org/), the [Serverless](https://www.serverless.com/) framework and AWS lambda as our serverless provider.

This is what we're going to cover today :)

## Getting Started

*( I’m gonna assume you have at least NodeJS installed :) )*

First we will need some dependencies to make it work on a serverless environments :

```
npm install -S serverless-http express serverless-apigw-binary
```

Now we are going to init a new NuxtJS project ( Choose the options that suit your needs when asked ) :

```
npx create-nuxt-app hello-world
```

Now cd into the directory :

```
cd hello-world
```

And then start the project :

```
npm run dev
```

You should see this if you go to http://localhost:3000/ on your web browser :)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i5j117jeqixcsvgnmnqj.png)

## The 'Server' code

Now to make it runnable on our “custom” server, we are going to create a folder “server” at the root of the project and create a new file “index.js” inside it.

[index.js](https://github.com/Monisnap/nuxtjs-serverless-sample/blob/master/server/index.js)

The configuration is pretty straightforward.

We first import the required dependencies :

```js
const path = require("path");
const { loadNuxt } = require("nuxt");
const serverless = require("serverless-http");
const express = require("express");
```

Then we init the express app :

```js
const app = express();
```

Next we have the module.exports.nuxt which export the "handler" function that is going to be used by serverless.

Inside this export we have the first app.use which links the static assets from the directory where the assets are built to the /\_nuxt/ path ( this is the default path where Nuxt looks for the assets ) :

```js
app.use("/_nuxt", express.static(path.join(__dirname, ".nuxt", "dist", "client")));
```

We then get the Nuxt instance ( start indicates that we want to use the production package ) :

```js
const nuxt = await loadNuxt("start");
```

And we tell express to use it for every routes via the nuxt.render method which allows us to render each route based on our routes params, pretty useful.

```js
app.use(nuxt.render);
```

In the last part we use the serverless-http package which will wrap our Express app to be understandable by Lambda. We also need to pass some configuration options to allow any content-type we wish to serve with our application :

```js
return serverless(app, {
   binary: [
     'application/javascript',
     'application/json',
     'application/octet-stream',
     'application/xml',
     'font/eot',
     'font/opentype',
     'font/otf',
     'image/jpeg',
     'image/png',
     'image/svg+xml',
     'text/comma-separated-values',
     'text/css',
     'text/html',
     'text/javascript',
     'text/plain',
     'text/text',
     'text/xml',
     'font/woff',
     'font/woff2'
   ]
 })(request, context)
}
```

And this is it for the server code ! ( pretty easy right ? :))

## The Serverless part

Now let’s focus on the Serverless part.
Create a the root of the directory a file named serverless.yml.

[serverless.yml](https://github.com/Monisnap/nuxtjs-serverless-sample/blob/master/serverless.yml)

This is a pretty basic Serverless configuration :

First we define the service name ( used by lambda and API Gateway ) :

```yml
service: hello-world
```

Then we have the provider specific configurations :

- The provider name ( AWS in our case ),
- The nodejs runtime we want our code to run on
- The AWS lambda region where our lambdas will we provided

```yml
provider:
  name: aws
  runtime: nodejs12.x
  region: eu-west-1
```

We then have a custom variable set called apigwBinary which will be used by the plugin serverless-apigw-binary to let API Gateway know that we want to support binaries as responses.

```yml
custom:
  apigwBinary:
    types:
      - "*/*"
```

The next block define our function and the associated http events, we set two event, one will be handling all the requests from the root path and the other any additional path we want to support.

```yml
functions:
  nuxt:
    handler: server/index.nuxt
    memorySize: 512
    events:
      - http:
          path: /
          method: any
      - http:
          path: /{proxy+}
          method: any
```


And finally we tell Serverless we want to use the serverless-apigw-binary plugin :)

```yml
plugins:
 - serverless-apigw-binary
```

Before deploying the app, you need to have an account ( you can create one for free [here](https://aws.amazon.com/resources/create-account/)) and [set up](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) the AWS cli tool ( you can use the free tier with lambdas ) 


Once you’ve configured the cli tool, you are ready to build and deploy the app :

```
npm run build && sls deploy
```

And you can now hit the url shown in the console output and you should see your Nuxt app appearing :)

```
https://<api-gateway-id>.execute-api.<region>.amazonaws.com/dev/
```

There is a little caveat though... If you check your web browser console you should see a lot of console errors related to the loading of resources.
Nuxt tries to load them from the /_nuxt/ path but API gateway exposes the lambda with a “stage” prefix ( e.g dev ), so obviously it does not find them.

To effectively fix this problem, you need to add a custom domain name to API gateway ( You will need a domain name and a tls certificate ) which will allow you to serve the app from the root of your domain.


## Conclusion

Serverless allows us to compile and serve our Nuxt application at a very low cost and with no worries about scalability ( among other benefits ).
Of course it comes with some drawbacks which can easily be overcome.

Drop a comment if this has been useful or if you have any questions :)

Have a good one !