Elvis Ciotti
2 min readSep 22, 2024

Multi page app con AWS lambda with Node, express.js and AWS lambda, ESJ templating

What I needed

I needed to host an old-style multi-page app for a website with many different URLs exposed to search engines using node and AWS lambda. I liked to idea of having JSX templates, but the overcomplication of using babel/react server or Next.js was making this too complicated. There is a serverless plugin to hook them, but it didn’t work well from the start and took too much time to make it work. My experience as a developer is keeping things simple. My app wasn’t going to have complicated layout, but more likely the need of strong customisation, so I ended up just creating simpler pluggable independent and replaceable libraries, making the project simpler, easier to update so overall a better choice.

The stack

Config

define your app file with routes in a file under src/app.js

import express from 'express';
const app = express();

app.get('/', async (req, res) => {
// ...
});

Then define a file src/app-local.js to launch the express server and browse locally. That could be launched from IntelliJ directly. Make sure the working directory is the root one, and not src, to have the same file loading relative path as the AWS lamda hosting

import app from "./app.js";

const port = 3000;

app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})

Now you can create the lambda handler src/frontend.js , that loads the app from the shared `app.js` and returns the handler created with serverless-http

import serverless from 'serverless-http';
import app from "./app.js";

export const handler = serverless(app);

The missing bit is the serverles.yaml config

service: ...
frameworkVersion: '3'

provider:
name: aws
runtime: nodejs20.x
region: us-east-1
stage: prod
deploymentMethod: direct
memorySize: 128 # optional, in MB, default is 1024
timeout: 5
environment:
APP_AWS_REGION: us-east-1
LOG_LEVEL: debug
AWS_LAMBDA_LOG_LEVEL: warn
POWERTOOLS_LOG_LEVEL: warn
iam:
role:
statements:
- Effect: "Allow"
Action:
- dynamodb:GetItem
- dynamodb:BatchGetItem
- dynamodb:Query
- dynamodb:Scan
- dynamodb:PutItem
Resource:
- 'arn:aws:dynamodb:${self:provider.environment.APP_AWS_REGION}:${self:provider.environment.APP_AWS_ACCOUNT_ID}:table/*'


functions:
frontend:
handler: src/frontend.handler
events:
- http:
path: "/"
method: ANY
- http:
path: "{proxy+}"
method: ANY

Note that it needs two events to route all the traffic to the handler. Just the second one did not work for me.

Clap if useful, follow me for more

Elvis Ciotti
Elvis Ciotti

Written by Elvis Ciotti

Software Contractor — Java, Spring, k8s, AWS, Javascript @ London - hire me at https://elvisciotti.github.io/

No responses yet