Nodejs serverless monolithic architecture api with typescript and aws lambda
View Demo
·
Report Bug
·
Request Feature
Table of Contents
This repository provides a well-structured, production-ready template for building robust and scalable Node.js serverless APIs using AWS Lambda. It leverages TypeScript for enhanced type safety and maintainability, Prisma for efficient database interactions, and AWS Key Management Service (KMS) for secure data encryption.
-
Handlers receive dependencies (like database clients or logging services) as function parameters instead of relying on global state or singletons. This promotes cleaner code, easier testing, and improved modularity.
-
Handler functions are designed as pure functions: given the same inputs, they always produce the same outputs and have no side effects like modifying global state. This simplifies reasoning about their behavior and makes them more testable.
-
Strong typing enforces data consistency, reducing runtime errors and improving code reliability. It also enhances developer experience with code completion and easier refactoring.
-
This folder houses reusable modules and utilities, promoting code reusability and separation of concerns. Developers can easily import and leverage these modules across different projects.
-
Ideal for smaller backend projects or rapid prototyping. All functionalities reside within a single codebase for easy deployment and maintenance.
-
-
Prisma Integration - Simplifies database interactions with a type-safe and performant ORM (Object-Relational Mapper). It reduces boilerplate code and ensures data consistency.
-
AWS KMS Integration - Manages encryption keys centrally for secure data storage and transmission. It complies with data security best practices.
-
- This script automates code transpilation, build, bundling, and deployment. It prompts developers for the S3 bucket name and Lambda function name, streamline the deployment process.
This section walks you through the prerequisites for development and guides you in cloning this repository, deploying your API to AWS Lambda, and customizing configurations.
-
aws-cli: must configure aws cli with an IAM user having permission to update lambda invoke lambda and interact with the s3bucket.
-
7zip: must install 7zip and add it's path to environment variable of your local machine.
-
mongodb:
- must create a mongodb database and include it's connection url in .env and it's encrypted value in your lambda function's environment variable as well.
- additionally please add
db name
at the end of your mongodb string for eg.mongodb+srv://username:password@clusterName.uniqueID.mongodb.net/
db name?retryWrites=true&w=majority&appName=clusterName
. - also refer to this article before trying to connect your mongodb with lambda, article.
-
aws-resources: create a lambda function from aws console and add a role to that lambda which has the policy of an kms key which will be used for encrypting and decrypting data, also create an s3 bucket for uploading your lambda code, remember their name.
-
environment-variables: there are 2 environment variables we have to use locally while building the code which is DATABASE_URL self explanatory and PRISMA_BINARY_TARGET read about it in this article prisma-binaries
Clone the project
git clone https://github.com/dipanshSaxenaVivo/monolith_lambda_api.git
Go to the project directory
cd monolith_lambda_api
Install dependencies
npm install
before deploying the lambda please make valid schema inside src/prisma/schema if you want work with different collection, otherwise keep everything same
Generate prisma and sync your schema with database
npx prisma generate && npx prisma db push
Deploy to lambda
npm run deploy
-
Dependency Injection (DI) promotes cleaner, more modular code by decoupling the creation of dependencies from their usage. This improves testability and maintainability, making it easier to manage and scale the application.
-
- Database Client: Prisma for database interactions.
- Logger: A custom logger for tracking application events.
- Cryptography: For handling encryption and decryption tasks using AWS KMS.
Dependencies are injected into handlers via a centralized injector module, which configures and provides these dependencies to the appropriate handlers based on their needs.
we expose a applier function from our dependency module which simply adds it's functionality into the key of dependency container.
example of applier function
/**
* applies KMS dependency to the given dependency container
*
* @param {Omit<IDependencyContainer, 'KMS'>} DC
* @returns {IDependencyContainer}
*/
export const apply_kms = (
DC: Omit<IDependencyContainer, "KMS">
): IDependencyContainer => {
console.log("apply kms called");
return { ...DC, KMS: KMS };
};
example of applying dependency
const injector_applied_kms = apply_kms({})
now, if we want to apply another dependency for our handlers
we just have to pass injector_applied_kms
to the corresponding dependency's applier function.
example of applying prisma dependency
const injector_applied_prisma_and_kms = apply_kms(injector_applied_kms)
usually we would have to apply all available dependency to all of our handlers so we can instantiate our injector with all applier functions like.
example of applying all dependencies
let injector = apply_prisma(apply_console_logger(apply_kms({} as any)));
just be aware of lifecycle methods of aws lambda cold start, warm and shutdown phase because our dependencies are instantiated in cold start and they are available till the shutdown phase of lambda, they don't instantiate again in warm phase.