Production ready Typescript Serverless project configuration

serverless developer & architect at Elva
Search for a command to run...

serverless developer & architect at Elva
No comments yet. Be the first to comment.
For a long time, both professionally with clients in my consulting work and in the AWS community, I've been evangelizing the "serverless" paradigm shift. To me, it's crystal clear that you should focus on building what differentiates your business, a...

AWS StepFunctions is a fantastic service for orchestrating complex workflows. But testing StepFunctions has always been... tricky. You could execute the entire state machine, observing side effects and final outcomes, but this approach often feels li...

AWS WAF, the managed Web Application Firewall, is a commonly used service to secure APIs, load balancers, and applications. But because of how the pricing model is set up for WAF, the costs can quickly spiral out of control when adhering to the AWS b...

In this post, we'll look at some of the most popular frameworks for building serverless applications on AWS, including their features, strengths, and weaknesses. We'll also look at examples of how each framework can be used to build an example applic...

One of the most significant benefits of serverless is that the development teams can fully own and manage all of their infrastructure themself. Keeping all of that infrastructure in the same CloudFormation stack can sometimes be troublesome however, ...

Before we get started - if you're just here because you want a plug 'n' play starter, here's a project generator and here's an example repository! π
Getting Typescript to run is usually pretty simple. You just follow a few lines of instructions from the first google link. Getting the project from that point to something that you can deploy with confidence is often surprisingly hard though. To get there, you'll at the very least want to have testing and linting set up. Tree shaking is another thing you'll probably want to be able to drastically reduce your bundle sizes.
This is going to be a walkthrough of my opinionated approach to achieving a simple but extensible setup that accomplishes these simple goals for a Serverless AWS project.
Let's start off by creating a new project. Make sure you're in an empty directory and run npm init -y. We're going to need a few tools to set this up:
Let's add those as devDependencies right of the bat as well
yarn add eslint jest @types/jest serverless serverless-webpack ts-jest ts-loader typescript webpack -D
We'll need some example Typescript code to run in our app. I prefer placing the application code in an src folder and the test code in a tests folder to keep them separate.
// src/myFunction.ts
export const handler = async (event: any, context: any) => {
return 'Hello from handler!';
};
// tests/myFunction.test.ts
import { handler } from '../src/myFunction';
describe('[myFunction]', () => {
it('should not crash', async () => {
await handler({}, {});
});
});
In order to transpile and run the Typescript code, we'll need a tsconfig.json. We can generate one to start from by running yarn tsc --init. Let's change both the target and lib to "ES2020" as we want to be able to use modern ECMAScript features.
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": ["ES2020"], /* Specify library files to be included in the compilation. */
"strict": true, /* Enable all strict type-checking options. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
All we need now to be able to start developing and testing our app is to add a Jest configuration file. The default settings for Jest are pretty sensible to we don't have to care about most of those right now except for setting preset to "ts-jest" and the testEnvironment to "node".
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
We can now finally run our tests with yarn jest to confirm that everything seems to work so far.

Excellent, our tests are passing! π
We'll be using ESLint for static code analysis and code style enforcement. ESLint configurations can quickly become a mess and it's by nature very subjective so let's keep it simple here. When we run yarn eslint --init we'll get to answer a few questions about our preferences. In the image below I chose to create a small custom set of rules rather than using one of the "popular style guides" but it's completely up to you.

This is how mine ended up looking:
// .eslintrc.js
module.exports = {
env: {
es2021: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
rules: {
indent: ['error', 2],
'linebreak-style': ['error', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
},
ignore: ['webpack.config.js'],
};
The AirBnb style guide has a few conflicts with our Typescript setup out of the box so if you choose to extend AirBnb style guide you'll have to do these steps as well:
"jest" to the env configuration'plugin:import/typescript' to extends & import to plugins to get rid of module resolution errors'import/extensions': 'off' to rulesLastly, we need to configure Webpack and Serverless in order to build and deploy our app. For the Webpack configuration, we can use a pretty simple setup where we specify ts-loader to be used as the loader for Typescript files.
// webpack.config.js
const path = require('path');
const slsw = require('serverless-webpack');
module.exports = {
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
entry: slsw.lib.entries,
resolve: {
extensions: ['.ts', 'tsx']
},
target: 'node',
module: {
rules: [
{
test: /\.(tsx?)$/,
loader: 'ts-loader',
exclude: [
[
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, '.serverless'),
path.resolve(__dirname, '.webpack'),
],
],
},
],
},
};
and finally, for the Serverless configuration we'll, in addition to some basic information about our app, add the following:
serverless-webpack to the list of pluginspackage.individually to true in order to bundle each function separately so that we can reduce the bundle size for each function by only including exactly what's needed to run ityarn so that it'll respect our lock file.// serverless.yml
service: sls-ts
plugins:
- serverless-webpack
package:
individually: true
custom:
webpack:
packager: yarn
provider:
name: aws
region: eu-north-1
runtime: nodejs14.x
functions:
myFunction:
handler: src/myFunction.handler
That's it! Hit yarn serverless deploy to deploy the project! We can verify that it works by running yarn serverless invoke -f myFunction

Project configuration is highly subjective and the complexity often grows as the projects grows but I hope this can serve as a starting point to build upon!
If you enjoyed this guide and want to see more, follow me on Twitter at @TastefulElk where I frequently write about serverless tech, AWS and developer productivity!
Happy hacking! π