<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JasonLy</title>
    <description>The latest articles on DEV Community by JasonLy (@jasonlyy).</description>
    <link>https://dev.to/jasonlyy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F417917%2Faa5bf2e6-e98c-4015-9e44-73663805afbc.png</url>
      <title>DEV Community: JasonLy</title>
      <link>https://dev.to/jasonlyy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jasonlyy"/>
    <language>en</language>
    <item>
      <title>AWS CDK: Boilerplating to Build Node.js Apps in TypeScript</title>
      <dc:creator>JasonLy</dc:creator>
      <pubDate>Sat, 27 Jun 2020 11:31:56 +0000</pubDate>
      <link>https://dev.to/jasonlyy/aws-cdk-boilerplating-to-build-node-js-apps-in-typescript-5a6a</link>
      <guid>https://dev.to/jasonlyy/aws-cdk-boilerplating-to-build-node-js-apps-in-typescript-5a6a</guid>
      <description>&lt;h1&gt;
  
  
  AWS CDK: Boilerplating to Build Node.js Apps in TypeScript
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/home.html"&gt;AWS Cloud Development Kit (CDK)&lt;/a&gt; enables provisioning of infrastructure using traditional programming languages you're famiilar with including TypeScript and Python. When I first started building in CDK, I remember looking for some quick start boilerplate code to quickly get started and build a serverless application in Node.js (TypeScript).&lt;/p&gt;

&lt;p&gt;However, some issues I encountered trying gettings started includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There wasn't many templates to start with&lt;/li&gt;
&lt;li&gt;Many code examples were in JavaScript and not TypeScript&lt;/li&gt;
&lt;li&gt;There were also some unique issues around bundling and deployment which I wished I knew about earlier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how my projects are setup to get me up and running quickly. Here's the  &lt;a href="https://github.com/JasonLyy/cdk-ts-boilerplate"&gt;GitHub&lt;/a&gt; for a TL;DR and a basic example for what will be described below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Run this command inside a empty directory to setup a CDK project in that folder:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk init --language typescript
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;While we are here, lets setup some Linting and Formatting using ESLint and Prettier (&lt;a href="https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project"&gt;Credits&lt;/a&gt;)&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lets update &lt;code&gt;package.json&lt;/code&gt; for linting by adding &lt;code&gt;"lint": "eslint '*/**/*.{js,ts}' --quiet --fix"&lt;/code&gt; to &lt;code&gt;scripts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lets also create a &lt;code&gt;.eslintrc.js&lt;/code&gt; file: &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
    parser: "@typescript-eslint/parser", // Specifies the ESLint parser
    parserOptions: {
      ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
      sourceType: "module", // Allows for the use of imports
    },
    extends: [
      "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
      "prettier/@typescript-eslint", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
      "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
    ],
    rules: {
      // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
      // e.g. "@typescript-eslint/explicit-function-return-type": "off",
    },
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And finally, lets create a &lt;code&gt;.prettierrc.js&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
    semi: true,
    trailingComma: "all",
    singleQuote: true,
    printWidth: 120,
    tabWidth: 4
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Quick Project Refactor
&lt;/h2&gt;

&lt;p&gt;Now I personally don't like how the CDK project is initially structured so I refactored it to distinctly seperate infrastructure code and actual code. &lt;/p&gt;

&lt;p&gt;Lets create a new folder called &lt;code&gt;infra&lt;/code&gt; which will hold all the infrastructure code. We will move the file in &lt;code&gt;bin&lt;/code&gt;to &lt;code&gt;infra&lt;/code&gt; (I also renamed it to &lt;code&gt;app.ts&lt;/code&gt;) and the file in &lt;code&gt;lib&lt;/code&gt; to &lt;code&gt;infra&lt;/code&gt;. Once we've done that we can remove both the &lt;code&gt;bin&lt;/code&gt; and &lt;code&gt;lib&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;cdk.json&lt;/code&gt; and update &lt;code&gt;"app": "npx ts-node bin/[your_orignal_file_in_bin].ts"&lt;/code&gt; to &lt;code&gt;"app": "npx ts-node infra/app.ts"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now the actual code and other non-infrastructure related files will be located in &lt;code&gt;src&lt;/code&gt;. So lets create a &lt;code&gt;src&lt;/code&gt; folder. Inside that &lt;code&gt;src&lt;/code&gt; folder, lets also create a folder called &lt;code&gt;lambda&lt;/code&gt; which will hold all lambda functions (if the project uses Lambda). Each Lambda will be represented inside that folder as &lt;code&gt;lambda_name/index.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While we are here, lets also update the &lt;code&gt;.gitignore&lt;/code&gt; for later by removing &lt;code&gt;*.js&lt;/code&gt;, and adding &lt;code&gt;dist&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up CDK Deploy in package.json
&lt;/h2&gt;

&lt;p&gt;CDK will throw you an error if you try to deploy with mismatching versions of &lt;code&gt;aws-cdk&lt;/code&gt; or if CDK dependencies you use have different versions (e.g. &lt;code&gt;"@aws-cdk/aws-sns": "^1.47.0"&lt;/code&gt; and &lt;code&gt;"@aws-cdk/aws-lambda": "^1.46.0"&lt;/code&gt;). This can cause some issues in some CI/CD pipelines where you have to run &lt;code&gt;npm install -g aws-cdk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A quick fix to those issues is to add something like this to your &lt;code&gt;package.json&lt;/code&gt; file: &lt;code&gt;"deploy": "cdk deploy your_stack_name"&lt;/code&gt; to ensure that it uses the CDK defined in your &lt;code&gt;package.json&lt;/code&gt; already. Also everytime you deploy, you must ensure that any CDK dependencies are also of the same version.&lt;/p&gt;
&lt;h2&gt;
  
  
  CDK Bundling
&lt;/h2&gt;

&lt;p&gt;Although CDK intrastructure is built in TypeScript, it is actually executed using &lt;code&gt;ts-node&lt;/code&gt; to generate the infrastructure CloudFormation. However, CDK by default does not transpile TypeScript to JavaScript or bundles code together (even if you're just using JavaScript). This can become a problem if you're building your Lambdas in TypeScript or your Lambda may reference other files in different directories or require other files.&lt;/p&gt;

&lt;p&gt;Recently, CDK released the &lt;a href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-nodejs-readme.html"&gt;aws-lambda-nodejs&lt;/a&gt; construct which solves the exact problem described above. However, this module is still considered as 'experimental' and under 'active development'. Another solution is to use Webpack to transpile and bundle TypeScript.&lt;/p&gt;

&lt;p&gt;To get started with &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; lets install some dependencies.  To get started lets run: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev webpack webpack-cli rimraf builtin-modules ts-loader
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;rimraf&lt;/code&gt; is used to delete transpiled files and other CDK generated files which we might want everytime we run &lt;code&gt;npm run build&lt;/code&gt;. &lt;code&gt;builtin-modules&lt;/code&gt; will be used later to reduce deployment sizes. &lt;code&gt;ts-loader&lt;/code&gt; is used to transpile TypeScript.&lt;/p&gt;

&lt;p&gt;Now add this line below to your &lt;code&gt;package.json&lt;/code&gt; scripts:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build": "rimraf dist &amp;amp;&amp;amp; webpack"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will remove &lt;code&gt;dist&lt;/code&gt; where the transpiled files will go to later via webpack before running webpack.&lt;/p&gt;

&lt;p&gt;Now lets create a &lt;code&gt;webpack.config.js&lt;/code&gt; file with the following settings: &lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const path = require('path');
const fs = require('fs');
const nodeBuiltins = require('builtin-modules');

const lambdaDir = 'src/lambda';
const lambdaNames = fs.readdirSync(path.join(__dirname, lambdaDir));

const entry = lambdaNames.reduce((entryPoints, lambdaName) =&amp;gt; {
    const tsPath = path.join(__dirname, lambdaDir, `${lambdaName}/index.ts`);
    const jsPath = path.join(__dirname, lambdaDir, `${lambdaName}/index.js`);

    const isTsFile = fs.existsSync(tsPath);
    const isJsFile = fs.existsSync(jsPath);
    if (isTsFile) {
        entryPoints[lambdaName] = tsPath;
    } else if (isJsFile) {
        entryPoints[lambdaName] = jsPath;
    }

    return entryPoints;
}, {});

const externals = ['aws-sdk'].concat(nodeBuiltins).reduce((externals, moduleName) =&amp;gt; {
    externals[moduleName] = moduleName;
    return externals;
}, {});

module.exports = {
    entry,
    externals,
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: {
                    loader: 'ts-loader',
                    options: { onlyCompileBundledFiles: true },
                },
                exclude: /node_modules/,
            },
        ],
    },
    output: {
        path: path.join(__dirname, 'dist', lambdaDir),
        libraryTarget: 'commonjs',
        filename: '[name]/index.js',
    },
    target: 'node',
    optimization: {
        minimize: false,
    },
    devtool: 'inline-cheap-module-source-map',
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This webpack is configured to bundle for each Lambda (the entry point) based on the following pattern: &lt;code&gt;src/lambda/[lamba_name]/index.ts&lt;/code&gt;. It will also support Lambdas which are not TypeScript by also looking for &lt;code&gt;src/lambda/[lamba_name]/index.js&lt;/code&gt;. TypeScript Lambdas will be bundled via &lt;code&gt;ts-loader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the Lambda Execution Environment, it will already have &lt;code&gt;aws-sdk&lt;/code&gt; and other built in node modules so they will be excluded from the bundle (to reduce deployment size).&lt;/p&gt;

&lt;p&gt;The bundle will be outputted to the &lt;code&gt;dist&lt;/code&gt; folder in the exact same pattern as above. Also minimisation is disabled and &lt;a href="https://webpack.js.org/configuration/devtool/"&gt;source mapping&lt;/a&gt; using &lt;code&gt;inline-cheap-module-source-map&lt;/code&gt; is enabled (to map transpiled code back to TypeScript lines) as it is running on the backend.&lt;/p&gt;

&lt;p&gt;To get Lambda to work with source mapping you must include source map support in every Lambda To do this, you first must install &lt;code&gt;source-map-support&lt;/code&gt; by running:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install source-map-support
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then for each Lambda you must add &lt;code&gt;import  'source-map-support/register';&lt;/code&gt; to the top of every file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;As you develop with CDK, you'll find that your project components and structure varies depending on your needs. I hope this gives you a good starting point to get something running when working with a relatively new tool like CDK.&lt;/p&gt;

</description>
      <category>cdk</category>
      <category>lambda</category>
      <category>aws</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
