├── jest.setup.js ├── .gitignore ├── variables.tf ├── local.tf ├── lambda-fns ├── __tests__ │ └── example.ts └── example.ts ├── jest.config.js ├── tsconfig.json ├── main.tf ├── package.json ├── LICENCE ├── webpack.config.js └── README.MD /jest.setup.js: -------------------------------------------------------------------------------- 1 | jest.mock('typescript-is') 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | .terraform 5 | .cache 6 | *.tfstate 7 | *.tfstate.backup 8 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "prefix" { 2 | description = "All resources will be prefixed with this name, to avoid naming collisions on AWS" 3 | } 4 | -------------------------------------------------------------------------------- /local.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | lambdas = toset( 3 | [for file_name in fileset(path.module, "dist/*.zip") 4 | : replace(basename(file_name), ".zip", "")] 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /lambda-fns/__tests__/example.ts: -------------------------------------------------------------------------------- 1 | import { ExampleHandler } from '../example' 2 | 3 | describe('ExampleHandler', () => { 4 | test('return Hello world', async () => { 5 | const result = await ExampleHandler({ name: 'Julian' }) 6 | expect(result).toEqual({ msg: 'Hello Julian'}) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | roots: ['lambda-fns'], 5 | setupFiles: ['/jest.setup.js'], 6 | globals: { 7 | 'ts-jest': { 8 | tsConfig: 'tsconfig.json', 9 | importHelpers: true, 10 | compiler: 'ttypescript' 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /lambda-fns/example.ts: -------------------------------------------------------------------------------- 1 | import { Handler } from 'aws-lambda' 2 | import { assertType } from 'typescript-is'; 3 | 4 | type MyEvent = { 5 | name: string 6 | } 7 | 8 | export async function ExampleHandler(event: MyEvent) { 9 | // validate event at runtime 10 | assertType(event) 11 | 12 | return { msg: `Hello ${event.name}` } 13 | } 14 | 15 | exports.handler = ExampleHandler 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitAny": true, 5 | "removeComments": true, 6 | "preserveConstEnums": true, 7 | "lib": ["es2020"], 8 | "esModuleInterop": true, 9 | "sourceMap": true, 10 | "plugins": [ 11 | { "transform": "typescript-is/lib/transform-inline/transformer" } 12 | ], 13 | "types": [ 14 | "node", 15 | "jest" 16 | ] 17 | }, 18 | "exclude": ["node_modules", "**/*.test.*"] 19 | } 20 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "iam_for_lambda" { 2 | name = "${local.prefix}-iam_for_lambda" 3 | 4 | assume_role_policy = < /\.(t|j)s$/.test(item)) 9 | .filter(item => !/\.d\.(t|j)s$/.test(item)) 10 | .reduce((acc, fileName) => ({ 11 | ...acc, 12 | [fileName.replace(/\.(t|j)s$/, '')]: `./${dir}/${fileName}` 13 | }), {}) 14 | const distFolder = 'dist' 15 | const distPath = path.resolve(process.cwd(), distFolder) 16 | 17 | module.exports = { 18 | entry, 19 | mode: 'production', 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.tsx?$/, 24 | loader: 'ts-loader', 25 | exclude: /node_modules/, 26 | options: { 27 | getCustomTransformers: program => ({ 28 | before: [typescriptIsTransformer(program)] 29 | }) 30 | }, 31 | }, 32 | ], 33 | }, 34 | resolve: { 35 | modules: ['node_modules'], 36 | extensions: [ '.tsx', '.ts', '.js', '.json' ], 37 | }, 38 | target: 'node', 39 | stats: 'minimal', 40 | optimization: { 41 | usedExports: true, 42 | }, 43 | mode: 'production', 44 | plugins: [ 45 | { 46 | apply: compiler => { 47 | compiler.hooks.done.tap( 48 | 'ZipPlugin', 49 | (a,b,c) => { 50 | Object.keys(entry).forEach(name => { 51 | exec(`zip ${name}.zip -r ${name}.js`, { cwd: distPath }) 52 | }) 53 | exec(`rm *.js`, { cwd: distPath }) 54 | console.info( 55 | 'produced deployment packages:\n\n', 56 | Object.keys(entry).map(name => ' 💾 ./' + path.join('./', distFolder, `${name}.zip`)).join('\n\n '), 57 | '\n' 58 | ); 59 | }); 60 | } 61 | } 62 | ] 63 | }; 64 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Typescript AWS Terraform Starter 2 | 3 | A boilerplate for developing serverless applications with typescript and terraform. 4 | 5 | - ✅ [`webpack`](https://webpack.js.org/) to compile deployment packages. 6 | - ✅ [`jest`](https://jestjs.io/) and for testing 7 | - ✅ [`typescript-is`](https://github.com/woutervh-/typescript-is) to validate event input 8 | - ✅ Minimal [terraform](terraform.io) configuration to manage deployment. 9 | 10 | ### Prerequisites: 11 | 12 | - [terraform](terraform.io) 13 | - [The AWS cli](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html). 14 | 15 | I believe both can be installed via brew (`brew install terraform aws) 16 | 17 | ## Commands 18 | 19 | - `npm run build` - Turns all typescript files in `lambda-fns` into lambda deployment packages (To deploy run ) 20 | - `npm run test` - Runs all tests 21 | - `npm run test:watch` - Rerun tests when files change 22 | - `npm run plan` - Display terraform deployment plan. Shows you what AWS resources will be created. 23 | - `npm run deploy` - Deploys project to aws with terraform 24 | - `npm run destroy` - Tear down project on aws with terraform 25 | 26 | 27 | ### Adding more Lambdas 28 | 29 | 1. Simply duplicate `lambda-fns/example.ts` and edit the file and filename. 30 | 2. Run `npm run deploy` and all files in `lambda-fns` will be deployed on AWS as lambda handlers. 31 | 32 | ### Validating lambda input 33 | 34 | This boilerplate uses `typescript-is` to check types at runtime, you can use this as an easy way to validate lambda input, the example handler at `lambda-fns/example.ts` uses `assertType(event)` to validate the type of the event. 35 | 36 | ### Testing 37 | 38 | This repository uses `jest` and `ts-jest`. 39 | Every lambda function should have an accompanying test file in `lambda-fns/__tests__`. 40 | 41 | ### Give feedback or make suggestions 42 | 43 | If you have suggestions for what can be improved, [please open an issue](https://github.com/juliankrispel/typescript-aws-lambda-terraform/issues/new) 44 | 45 | Made by [Julian](https://jkrsp.com/), [follow me on twitter](https://twitter.com/juliandoesstuff) 46 | --------------------------------------------------------------------------------