Introduction
Over the years, I've worked on numerous projects. Each was unique, but they all shared a common need: standards. A way to organize code, maintain quality, and make development smoother for everyone involved.
Years have passed, and those standards evolved into something I'm proud to share. This guide isn't about complex architectural patterns or deep AWS knowledge. It's about the small things that make our daily work with AWS Lambda better.
From organizing your repository to adding the right tools, I'll show you how I like to work with Node.js and AWS Lambda. These are practices I've been using for years, refined through real projects and real teams.
Let's make your Lambda development experience better, together.
Repository Structure
A well-organized repository is the foundation of any successful Lambda project. Let's look at a structure that has worked well for me across multiple projects:
Core Directories
:/src
- Each function gets its own directory (e.g.,
,service-a
)service-b
- Functions contain their handler, tests, and configuration files
- Each function gets its own directory (e.g.,
:/infra
: AWS CDK stacks/cdk
: SST framework configurations/sst
- [insert your preferred Infrastructure as Code tool š]
Function Structure
Inside each function directory:
āāā service-a/post/ āāā event.json # Test events āāā handler.ts # Main Lambda handler āāā index.ts # Function exports āāā index.test.ts # Unit tests
Environment Management
Rather than managing multiple
.env
files, I prefer using AWS SSM Parameter Store for environment variables. Here's why:
- Security: Values are encrypted at rest
- Version Control: Parameter history is maintained
- Access Control: Fine-grained IAM permissions
- Organization: Hierarchical structure using paths
This structure keeps things organized while maintaining flexibility for growth. Each piece has its place, making it easier for teams to navigate and maintain the codebase.
Code Quality Tools
Code quality isn't about personal preferences, it's about team productivity. Writing consistent, maintainable code across a team is hard. That's why I'm a big fan of automated code quality tools.
ESLint Configuration
ESLint is our code quality. It catches potential bugs, enforces best practices, and maintains consistent patterns across your Lambda functions.
I use ESLint to:
- Catch common mistakes (unused variables, undefined references)
- Enforce TypeScript best practices
- Prevent deployment of
statementsconsole.log
- Maintain consistent import ordering
- Ensure proper error handling
Prettier Setup
While ESLint handles code quality, Prettier takes care of code formatting. It removes all the mental overhead of manually formatting code. No more discussions about:
- Where to put spaces
- When to break lines
- How to format long function chains
- Whether to use single or double quotes
Why They Work Together
ESLint and Prettier are like good cop, bad cop:
- Prettier automatically formats your code
- ESLint makes sure it's not just pretty, but also correct
- They run on save and before commits
- Your IDE shows errors and warnings in real-time
Note: All these tools are pre-configured in my repository template. Just clone and start coding, the setup is already done for you.
This combination means you can focus on solving problems instead of arguing about code style. The tools handle the rest.
Development Workflow
A solid development workflow catches issues early and keeps your codebase healthy. Here's how I structure my Lambda development process.
Git Hooks and Automation
I use Husky to automate quality checks at different git stages.
Pre-commit Hook
Before code is committed:
- Lints modified files
- Formats code with Prettier
- Prevents commits with
console.log
Commit Message Standards
Every commit message follows the Conventional Commits standard:
feat: add new user authentication fix: resolve cold start issues chore: update dependencies
This makes our git history readable and helps with automatic versioning.
Pre-push Hook
Before code is pushed:
- Runs all tests
- Ensures 80% code coverage
- Builds the project to catch compilation issues
This workflow means:
- No broken code reaches main branch
- Every change is tested
- Code style remains consistent
- Git history stays clean and meaningful
The goal isn't to make development harder, it's to catch issues when they're cheapest to fix: during development.
Middleware and Utilities
When working with AWS Lambda, middleware and utilities can significantly enhance your development experience and the quality of your functions. Two powerful tools in this space are Middy and AWS Lambda Powertools.
Implementing Middy
Middy is a lightweight middleware engine for AWS Lambda. It allows you to wrap your Lambda functions with additional functionality, making them more robust and easier to manage.
Basic setup
Setting up Middy is straightforward. You import it, wrap your handler function, and then chain middleware as needed. This modular approach keeps your main handler clean and focused on business logic.
Common middleware
Middy offers a variety of pre-built middleware for common tasks:
: Automatically parses JSON in request bodieshttpJsonBodyParser
: Normalizes HTTP events for easier handlinghttpEventNormalizer
: Provides consistent error responseshttpErrorHandler
: Adds security headers to responseshttpSecurityHeaders
Using these, you can quickly add robust functionality to your Lambda functions without cluttering your main code.
Custom middleware development
One of Middy's strengths is its extensibility. You can easily create custom middleware to encapsulate recurring logic across your functions. This might include authentication checks, input validation, or any other cross-cutting concerns specific to your application.
Some custom middleware I've built:
- SSM Parameter Store integration: fetches parameters from SSM and injects them into the AWS Lambda environment variables
- Secret Manager integration: retrieves secrets and makes them available to your function
- Database integration: mostly for databases connection management
- Feature flag middleware
AWS PowerTools
AWS Lambda Powertools is a suite of utilities designed to help implement best practices and increase developer velocity. Let's look at some of them.
Logging
Powertools provides structured logging out of the box. It includes features like log sampling and correlation IDs, making it easier to trace requests across distributed systems.
Tracing
The tracing utility integrates with AWS X-Ray, allowing you to visualize and analyze your application's behavior and performance.
Metrics
With Powertools' metrics utility, you can easily create and publish custom metrics to CloudWatch. This helps in monitoring your application's health and performance over time.
Best practices
Powertools encourages several best practices:
- Structured logging for better searchability
- Correlation IDs for request tracing
- Cold start labeling to distinguish between cold and warm starts
- Easy creation of custom metrics
By leveraging these tools, you can significantly improve the observability and maintainability of your Lambda functions. The goal is to spend more time on your business logic and less on boilerplate code.
Note: All these tools are pre-configured in my repository template. Just clone and start coding, the setup is already done for you. It demonstrates how these concepts come together in a real-world Lambda function setup.
Bundling and Optimization
Efficient bundling and optimization are paramount (can I say this word without this article being labeled as AI generated? š) for AWS Lambda functions. They help reduce cold start times, minimize costs, and improve overall performance.
Optimization Techniques
Code splitting: by separating your code into smaller, more manageable chunks, you can improve load times and reduce the overall bundle size. This is particularly useful for larger applications.
Dead code elimination: using tools like esbuild, we can automatically remove unused code from the final bundle. The
flag ensures that only the code that's actually used is included in the final package.--tree-shaking
Dependencies management: Carefully manage your dependencies to keep your function lean. Only essential modules are included in the
array, ensuring that only necessary dependencies are bundled with the function.nodeModules
By implementing these small optimization techniques, you can significantly improve the performance and efficiency of your AWS Lambda functions. The goal is to create lean, fast-executing functions that make the most of AWS Lambda's capabilities, and spend less of course š
Conclusion
Developing AWS Lambda functions doesn't have to be a complex task. By implementing the practices we've discussed, from organizing your repository structure to optimizing your deployment, you can create more efficient, maintainable, and scalable serverless applications.
These practices have evolved from real-world experience and have proven their worth across numerous projects. They're not just theory and can significantly improve your Lambda development experience.
Here's the link to my GitHub repository: https://github.com/Depaa/aws-lambda-supercharge-template š
If you enjoyed this article, please let me know in the comment section or send me a DM. I'm always happy to chat! āļø
Thank you so much for reading! š Keep an eye out for more AWS-related posts, and feel free to connect with me on LinkedIn š https://www.linkedin.com/in/matteo-depascale/.
References
- https://docs.aws.amazon.com/lambda/latest/dg/welcome.html
- https://middy.js.org/
- https://docs.powertools.aws.dev/lambda/typescript/latest/
Disclaimer: opinions expressed are solely my own and do not express the views or opinions of my employer.