Serverless Applications are now a trend these days for applications, it reduces the time effort and cost for our applications to be deployed, so diving deeply on what is the concept of Serverless, it is simply the process of outsourcing servers from third party providers that will handle all the deployment responsibilities using shared machines, it is a Functions as a Service (FaaS) which it only runs when clients has requests executed on the application and providers are charging costs base only on the number of requests which allows us to minimize the applications cost especially a low number of users are using our app.
Angular going Serverless!
A huge thank you for AWS that they have made serverless deployments Angular easier by providing extensions that we can deploy our applications on the fly. all we have to do is configure our angular application with instructions using yml configuration.
In this part, I will show you a step by step guide on how to deploy our Angular application on AWS Lambda, I assume here that you already have your awesome angular app that we can show the world by deploying on AWS.
Here are the other prerequisites we need before we start our setup.
AWS cli
you can install AWS cli using the link below
Serverless Framework
We will install this as dev dependency later on as we setup our Angular Project
Angular Universal
We will use this as our basis for creating our serverless configuration.
The First thing we need to do is install Angular Universal, we will use this as our basis for our setup as we create our own serverless configuration. to install Universal execute the following command.
ng add @nguniversal/express-engine
This will add support for SSR for our application by adding several files and additional scripts on package.json.
Now we will install our AWS dependencies and serverless framework so we have the capability to test our serverless locally.
This will install our serverless framework with serverless-offline to run our application offline.
npm install serverless serverless-offline serverless-apigw-binary -D
We will use aws-serverless-express to for our Lambda function.
npm install aws-serverless-express --save
Now in our project root directory, we will create a serverless.yml file that will contain our configuration necessary for deploying our application to AWS.
service: angular-serverless
plugins:
- serverless-apigw-binary
- serverless-offline
provider:
name: aws
runtime: nodejs12.x
memorySize: 192
timeout: 10
package:
exclude:
- ./**
include:
- "node_modules/aws-serverless-express/**"
- "node_modules/binary-case/**"
- "node_modules/type-is/**"
- "node_modules/media-typer/**"
- "node_modules/mime-types/**"
- "node_modules/mime-db/**"
- "dist/**"
- "lambda.js"
custom:
apigwBinary:
types:
- "*/*"
functions:
api:
handler: lambda.handler
events:
- http: GET {proxy+}
- http: GET /
We can see in our configuration that we our instructing what to Serverless what plugins we need for our application to run.
The next step is we will now configure our own configuration on building our serverless application, we will copy the content of server.ts generated by Angular Universal and create a file named serverless.ts file on our root directory.
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
export const app = express();
const distFolder = join(process.cwd(), 'dist/angular-serverless/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
app.set('view engine', 'html');
app.set('views', distFolder);
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
export * from './src/main.server';
The only difference here is that we have omitted the run function as Serverless apps will run upon request of clients and will not require ports.
We will now add tsconfig.serverless.json file that will be used as a configuration for building our Serverless app, the content will be the same as the generated tscconfig.server.json file of Angular Universal, we will just change the ourDir to ./out-tsc/serverless and under the files property we will change the server.ts to serverless.ts file.
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/serverless",
"target": "es2016",
"types": [
"node"
]
},
"files": [
"src/main.server.ts",
"serverless.ts"
],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
Now we want to add our own build configuration that will used our added settings for Serverless, under angular.json we will copy the server properties and rename it to serverless, and we will use our serverless.ts as our main file and tsconfig.serverless.json as our typescript configuration.
"serverless": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/angular-serverless/serverless",
"main": "serverless.ts",
"tsConfig": "tsconfig.serverless.json"
},
"configurations": {
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"sourceMap": false,
"optimization": true
}
}
},
And after successfully configuring our application, all we need to do is add our scripts in our package.json that will build our Serverless app, add the following scripts in our package.json file
"serve:serverless": "serverless offline start",
"build:serverless": "ng build --prod && ng run angular-serverless:serverless:production"
Now we will now allow the support of lambda functions as we deploy our Angular application AWS, we will create a lambda.js file on our root project that will contain the MIME Types and the aws-server-express that serve our distribution file.
const awsServerlessExpress = require("aws-serverless-express");
const server = require("./dist/angular-serverless/serverless/main");
const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware");
const binaryMimeTypes = [
"application/javascript",
"application/json",
"application/octet-stream",
"application/xml",
"image/jpeg",
"image/png",
"image/gif",
"text/comma-separated-values",
"text/css",
"text/html",
"text/javascript",
"text/plain",
"text/text",
"text/xml",
"image/x-icon",
"image/svg+xml",
"application/x-font-ttf",
];
server.app.use(awsServerlessExpressMiddleware.eventContext());
const serverProxy = awsServerlessExpress.createServer(
server.app,
null,
binaryMimeTypes
);
module.exports.handler = (event, context) =>
awsServerlessExpress.proxy(serverProxy, event, context);
and that's it, to test of our configuration will successfully build and run a serverless app we will run the npm run build:serverless.
We can see here that our application has been built successfully, we can run npm run serve:serverless to run our application locally.
We can see that our app is now running on local, and every request by the browser will be logged here as Serverless applications are on demand.
Ready on AWS!
we are now ready to deploy our application on AWS, we have configured that it can support lambda, now before we proceed, we must configure our access id and secret key as this will not allow us to deploy on AWS without a valid credentials, execute the following command and enter you access id and secret key.
aws configure
after successful configuration, we will add the script serverless:deploy on our package.json
execute the command npm run serverless:deploy and this will upload our compiled code and will provide a URL where we can access our Angular application.
and that's it! we have successfully deployed our Angular application using AWS Lambda.
for the repository of the code, checkout my project in Github.
Cheers and happy deploying!
After I have successfully done a "serverless deploy", I am able to see my lambda function in the console and cloudformation stacks, etc. However, by default it does not seem to have a function URL to view the deployed application. I have been trying to play around a bit to get a function URL but it end up in an error message "internal server error". Any ideas ?
Thanks for posting this. When I run "npm run serve:serverless" I get
Serverless Error ----------------------------------------
Unexpected "handler" function configuration: Expected object received 'lambda.serverless'
Any ideas?