DEV Community

Cover image for How to add fancy to your Amplify Project
JoLo
JoLo

Posted on

How to add fancy to your Amplify Project

In this part of the series, we want to make your Amplify project more fancy by adding Typescript to your Amplify functions and/or to turn your function- folder to a Monorepo. Just fancy sh*t ;)

This post also contains a project for #Hacktoberfest πŸ₯³

Why Typescript

We all know the benefits of Typescript. If you want to know more about Typescript, I encourage you to read the provided link.

Why Monorepo

Monorepo aka Multi-Project repository aka monolithic repositories is useful when synchronise packages along the functions. Monorepo also allow to share libraries across them.
As mentioned in the previous series, each function could be seen as package containing its own node_modules.
The following illustration hopefully demonstrates the problem.

image

Add Typescript to Amplify Functions

The following steps are used with the Amplify CLI and will install Typescript within its function-folder and add types to your Lambda- function

# Add a Lambda Function
amplify add function

? Select which capability you want to add: Lambda function (serverless function)
? Provide an AWS Lambda function name: concatenate
? Choose the runtime that you want to use: NodeJS
? Choose the function template that you want to use: Hello World

# Go the the src-Folder of the Function
cd amplify/backend/function/functionName/src

# Remove the replace .js with .ts
mv index.js index.ts

# Install Typescript and AWS SDK
npm install -D typescript
# Install AWS Lambda for better Typescript Handling
npm install aws-lambda 
# We still need some types
npm install -D @types/aws-lambda @types/node

# Add the Typescript configuration
touch tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Now, we have to fill the tsconfig.json

{
    "compilerOptions": {
      "declaration": false,
      "target": "ES2017",
      "module": "commonjs",
      "moduleResolution": "node",
      "rootDir": ".",
      "lib": ["es2017"],
      "typeRoots": ["node_modules/@types"],
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true,
      "noImplicitAny": true,
      "sourceMap": false,
      "strict": true,
      "baseUrl": ".",
      "outDir": "./"
    },
    "include": ["./**/*"],
    "exclude": ["node_modules"]
}
Enter fullscreen mode Exit fullscreen mode

Now, we add a script to the packages.json.

"scripts": {
    "tsc": "tsc"
  }
Enter fullscreen mode Exit fullscreen mode

Now, let's fill the index.ts with some logic.

import { Callback, Context, Handler } from 'aws-lambda';

interface TriggerEvent {
  key1: string;
  key2: string;
  key3: string;
}

export const handler: Handler<TriggerEvent, string> = (event: TriggerEvent, context: Context, callback: Callback<string>) => {
  const concatKey = `${event.key1} ${event.key2} ${event.key3}`;
  callback(null, concatKey);
};
Enter fullscreen mode Exit fullscreen mode

If you use VS Code, you should make use of the Autocompletion by simply pressing . or make use of the aws-lambda- module by changing custom TriggerEvent according to your trigger (e.g. SQSEvent,or DynamoDBStreamEvent etc.).
By running npm run tsc it should generate an index.js.
We could test in Amplify

# I had name my function 'concatenate'
amplify mock function concatenate
Enter fullscreen mode Exit fullscreen mode

Okay, cool now you know how to add Typescript to your Amplify Function.

We can create more... but wait... do we have to this all over again? Isn't that sort of inefficient?

If we create a new function, we would basically copy&paste the package.json and tsconfig.json in the new function. We would have x-times node_modules depending on the number of functions with x-times typescript and aws-lambda - module.

I hope you see the problem πŸ˜‰
That is why, we should consider using a Monorepo.

Create a Monorepo for your Amplify Functions

Probably, you have heard about Yarn Workspaces in combination with Lerna as a popular tool set when using a Monorepo. These are indeed great tools but we will use PNPM instead.
PNPM has not just a built-in support for multiple packages but also handles all your node_modules from your computer efficiently. Generally, you should use it for all your Node-Projects. Your Disk Drive will thank you for that 😁

Okay, let's continue.
First delete the node_modules- Folder. Please also remove the package-lock.json.
Then we will move the package.json and tsconfig.json the top-level of the function - folder.

# Install PNPM if you haven't
npm install -g pnpm

# You should still be in the your-fullstack-app/amplify/backend/function/functionName/src
rm -rf node_modules package-lock.json

# Move the packages.json and tsconfig.json to the function-folder
mv package.json tsconfig.json ../../

# And let's create a new Function and change the index.js to index.ts
amplify add function
Enter fullscreen mode Exit fullscreen mode

Your folder structure should look like this

.
β”œβ”€β”€ functionName1
β”‚   β”œβ”€β”€ amplify.state
β”‚   β”œβ”€β”€ functionName1-cloudformation-template.json
β”‚   β”œβ”€β”€ function-parameters.json
β”‚   └── src
β”‚       β”œβ”€β”€ event.json
β”‚       β”œβ”€β”€ index.ts
β”‚       └── package.json
β”œβ”€β”€ functionName2
β”‚   β”œβ”€β”€ amplify.state
β”‚   β”œβ”€β”€ function-parameters.json
β”‚   β”œβ”€β”€ functionName2-cloudformation-template.json
β”‚   └── src
β”‚       β”œβ”€β”€ event.json
β”‚       β”œβ”€β”€ index.ts
β”‚       └── package.json
β”œβ”€β”€ package.json
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Due to the nature of Amplify, the src- Folder will contain the resulting Lambda. If you have chosen a different srcDir when doing amplify init or amplify pull then that will be your folder. Here, I will use src.

For our second function, we will install a new package. But before we install the modules, we will move the content of src a level up.

# Make sure you are at 'functionName2'- level
mv src/** .
# Install i18n
pnpm install i18n
# Install its types
pnpm install -D @types/i18n

# Let's make that function work
cd src && mkdir locales
echo "{ \"title\": \"Translation\" }" > locales/en.json
echo "{ \"title\": \"Traduction\" }" > locales/fr.json
Enter fullscreen mode Exit fullscreen mode

Fill the index.ts up

import { Callback, Context, Handler } from 'aws-lambda';
import i18n from 'i18n';

interface TriggerEvent {
  lang: string;
}export const handler: Handler<TriggerEvent, string> = (event: TriggerEvent, context: Context, callback: Callback<string>) => {
  // init i18n
  i18n.configure({
    defaultLocale: 'en',
    directory: __dirname + '/locales',
  });
  // set locale according to event
  i18n.setLocale(event.lang);  // return the translation
  callback(null, i18n.__('title'));
};
Enter fullscreen mode Exit fullscreen mode

If you use VS Code, you probably notice the red squiggle under aws-lambdaand __dirname . That is because, we haven't installed the modules and the respective types.

Okay, install the modules globally for the functions.

# Go the function top-level folder
cd ../
# Install Modules using PNPM
pnpm install
Enter fullscreen mode Exit fullscreen mode

After that the red squiggles are gone πŸ‘Œ
So what happen if we run pnpm tsc?
Right, all the functions should have an index.js now. But.. wait... the src- Folder is empty.

Not a big problem because PNPM can run all script of all the package.json within the same Monorepo.
Alright, let's start

// in top-level package.json
"scripts": {
    "mv": "tsc && pnpm move -r", // this is now executing all package.json with 'mv'- script
    "tsc": "tsc",
 }
Enter fullscreen mode Exit fullscreen mode

We added now a mv- script to use PNPM for executing in all functions folder the move - script in their respective package.json.
Yet the package.json of the functions.

"scripts": {
  "move": "mv index.js src"
}
Enter fullscreen mode Exit fullscreen mode

Basically, with that script, we are moving the index.js to the src- folder.
Let's test it by running pnpm mv. Et voilΓ , your src- folder has only files which should be uploaded.
You could run amplify mock function functionName{1,2} in order to see if they are working.
Now, it is time to push it to the cloud.

amplify push
Enter fullscreen mode Exit fullscreen mode

The two functions should be now visible in your AWS Console.

image

Cool!
Try to test the Lambda within your AWS Console.
What? It doesn't work??🀨
You are saying the dependencies are missing?
Oh, so we should have put the node_modules- folder into the src, is that what you are saying? That is inacceptable, because it might be that modules are too big and we should keep the Lambda as tiny as possible otherwise the cold start would be too long...
But I got that covered. Another fancy thing we could do: Bundling the modules in our index.jsπŸ¦Έβ€β™€οΈπŸ¦ΈπŸ¦Έβ€β™‚οΈ

Bundling your Amplify Function

Why should we bundle and minify our code? Well, simply we reduce the Lambda package size by removing unnecessary characters and bundle the used modules in one place. Learn more about bundling code

There are some bundlers out there and I am pretty sure you all have heard of Webpack. Others like Snowpack or Parcel are also pretty popular.
But I really like esbuild which we will also use next.

# go to the function-folder level
# and install esbuild
pnpm install -D esbuild
Enter fullscreen mode Exit fullscreen mode

Adding a new scripts in the package.json

"scripts": {
  "tsc": "tsc",
    "mv": "tsc && pnpm move -r",
  "build": "esbuild ./**/src/index.ts --bundle --minify --platform=node --outdir=./ && pnpm move -r"
}
Enter fullscreen mode Exit fullscreen mode

Shortly said, it bundles and minifies all the index.ts-files (those are your entrypoint) of the functions to the latest index.js - node version.
You should know see an index.js with a single long line of code.
Great it looks good! Let's push it again amplify push.
Now, test again and yes! It worksπŸ₯³

add-fancy- Plugin and celebrating Hacktoberfest

Puh, that was actually a lot..
Here comes the best part. I have put that all into plugin, I called it amplify-add-fancy.

GitHub logo globaldatanet / amplify-add-fancy

A Plugin for AWS Amplify to make its function more fancy.

Amplify Add-Fancy

This is an AWS Amplify CLI - Plugin for making your function fancier by adding Typescript- Function or turning your function- folder to a Monorepo.

Why

We all know the benefits of Typescript but AWS Amplify's CLI does not support that out-of-the-box.

Furthermore, when creating many functions, each time you might pull the same library for all your functions. The solution for that are Workspaces (aka multi-package repositories, multi-project repositories, or monolithic repositories).

How

Make sure you have an Amplify-Project up and running.

git clone https://github.com/globaldatanet/amplify-add-fancy.git
amplify plugin add
Enter fullscreen mode Exit fullscreen mode

You get prompted where you need to put your absolute path and enter Yes afterwards.

? Enter the absolute path for the root of the plugin directory
/absolute/path/to/this/repository
? Run a fresh scan for plugins on the Amplify CLI pluggable platform Yes
Enter fullscreen mode Exit fullscreen mode

Now you can use follow commands

Command Description
amplify add-fancy typescript Add a Typescript-Function
…

You can clone the project and add it as a amplify-plugin (I will release it soon)πŸš€

git clone https://github.com/globaldatanet/amplify-add-fancy.git
amplify plugin add
Enter fullscreen mode Exit fullscreen mode

You get prompted where you need to put your absolute path and enter Yes afterwards.

? Enter the absolute path for the root of the plugin directory:
/absolute/path/to/this/repository
? Run a fresh scan for plugins on the Amplify CLI pluggable platform Yes
Enter fullscreen mode Exit fullscreen mode

Now you can use follow commands

Command Description
amplify add-fancy typescript Add a Single Typescript-Function
amplify add-fancy monorepo Turns your function- folder into a Monorepo. NOTE: This makes sense with more than two functions

Since it's #Hacktoberfest, I have labeled some issues as #Hacktoberfest. So any contribution is more than welcome πŸ€—

Thanks for reading and Happy Coding!

Top comments (3)

Collapse
 
erikash profile image
Erik Ashepa

Thanks for the Post!
I thought giving this a try but was wondering how do you debug the code locally if it's minified?
Would amplify function mock function{1,2} just work with source maps and everything?

Collapse
 
jk171505 profile image
Janusz Krawiec • Edited

I'm confused about function specific package.json's - looks like they are traveling from place to place :)...
How come that you have package.json in functionName1/src1 directory if you moved it to the top-level function dir? - am I missing something?

Collapse
 
jk171505 profile image
Janusz Krawiec

It's worth to note, that amplify pull removes everything except functions folders from function's top-level folder.