Building a Serverless Node.js Application with ESM and TypeScript in 2024
Have you ever encountered the frustrating challenge of trying to import an ESM module in Node.js, only to be met with a barrage of errors?
This common issue plagues many developers, especially when transitioning from older module systems to the modern ESM (ECMAScript Module) format. Perhaps you've found yourself stuck with import statements not working as expected, or the Node.js runtime throwing cryptic errors about module resolution. This hurdle is a significant one in the evolving JavaScript landscape.
In this comprehensive guide, we address these challenges head-on as we embark on a journey to construct a state-of-the-art Serverless Node.js application, utilizing ECMAScript Modules (ESM) and TypeScript. This article is designed as a progressive tutorial, where each section builds upon the previous one. Starting with a basic JavaScript setup, we incrementally integrate TypeScript and Serverless technologies, culminating in a sophisticated, deployable application. Whether you're new to modern JavaScript development or seeking to refine your skills with the latest standards, this guide will walk you through the entire process, layer by layer.
JavaScript
Initializing the Project:
Initialize your project with:
yarn init
Setting Up .gitignore
for Node.js:
Retrieve a Node.js-specific .gitignore
file:
curl https://www.toptal.com/developers/gitignore/api/node > .gitignore
Configuring package.json
:
Amend your package.json
as follows:
"type": "module",
"engines": {
"node": ">=16"
},
Installing Linting Tools:
Install xo
for project linting:
yarn add -D xo
Creating Editor Configuration:
Create a .vscode/settings.json
file:
{
"xo.format.enable": true,
"prettier.enable": false,
"eslint.enable": false,
"editor.defaultFormatter": "samverschueren.linter-xo",
"eslint.codeActionsOnSave.rules": null,
"[typescript]": {
"editor.defaultFormatter": "samverschueren.linter-xo"
},
// ... other settings
}
XO will automatically correct paths like "./lib"
to "./lib.js"
and "fs"
to "node:fs"
.
Adding a Lint Script:
Add a lint
script to package.json
:
{
"scripts": {
"lint": "xo"
}
}
TypeScript
Writing Code in TypeScript:
Write your code in TypeScript, retaining ".js"
extensions for imports.
Installing TypeScript Dependencies:
Install TypeScript essentials:
yarn add --dev typescript tsx @tsconfig/node20
Creating a tsconfig.json
:
Establish a tsconfig.json
with these settings:
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"moduleResolution": "node16",
"outDir": "dist",
},
}
Running your TypeScript Code:
Execute your code:
yarn tsx ./src/main.ts
Building your TypeScript Code:
Compile your TypeScript code:
yarn tsc
Serverless
Installing Dependencies:
Install necessary packages:
yarn add -D serverless serverless-offline serverless-plugin-typescript
Creating a serverless.yml
File:
Set up a basic serverless.yml
:
service: my-service
plugins:
- serverless-offline
- serverless-plugin-typescript
provider:
name: aws
runtime: nodejs18.x
region: eu-west-1
functions:
myFunction:
handler: src/handler.handler
events:
- http:
path: /
method: get
Creating an AWS Lambda Handler:
Develop a handler in src/handler.ts
:
export const handler = async (event: any) => ({
statusCode: 200,
body: JSON.stringify({
message: 'Hello, World!',
}),
});
Running Offline:
Test locally with:
yarn sls offline start
Deploying to AWS:
Deploy your application:
yarn sls deploy
Conclusion
Wrapping up, it's clear that building a modern Node.js app with ESM and TypeScript is more straightforward than you might think. It's all about getting the configuration right.
Check Out the Full Example:
For a hands-on look, visit my GitHub repo with the completed example from this guide: ESM Serverless Starter. It's a great resource for your projects.
What's Next?
This is just the start. I'll be writing more about using ESM, focusing on practical, real-world applications. Stay tuned for the next article on creating ESM modules.
Thanks for reading, and happy coding!