Advanced Serverless Techniques III: Simplifying Lambda Functions with Custom DynamoDB Middleware

The SaaS Enthusiast
7 min readFeb 9, 2024

--

Conceptual Art of Cloud Computing Evolution: A futuristic or abstract illustration showing the evolution of cloud computing, with a focus on serverless architectures. The image could include elements that represent the shift towards more abstracted and efficient computing models, with middleware as a key component.

Ever found yourself repeatedly typing the same lines of code across various functions and wondering if there’s a better way? If you’re uncertain about the optimal level of abstraction, it might seem easier to just copy and paste your code everywhere. However, this approach can lead to issues with maintainability, scalability, and refactoring that feel like a nightmare. It’s crucial to pay attention to the finer details, including how instances for your services are managed — preferably in the middleware rather than through repetitive code duplication. For instance, the need to initialize services like DynamoService, S3Service, or CognitoService in every single function can become a tedious and cluttered task. This not only makes your code look messy but also serves as a constant reminder of overlooked best practices. Yet, a simple trick could revolutionize your approach to organizing code, significantly cleaning up your lambda handlers to focus solely on business logic rather than initialization tasks.

Initialization in the Middleware

Integrating a custom middleware for managing DynamoDB operations is a brilliant move. It wraps your database logic neatly, offering a reusable solution across various Lambda functions. This middleware essentially provides an instance of your DynamoService directly into the Lambda context, streamlining access in your handlers.

Middleware Structure

Consider the middleware as a function that yields an object equipped with “before” and/or “after” properties. These functions are designed to run either before or after your Lambda handler, streamlining the process.

Injecting DynamoService

The “before” function within the middleware takes the lead by creating and appending an instance of DynamoService to the Lambda function’s context. This ingenious step allows the handler to effortlessly access the service through the context, enhancing simplicity and efficiency.

Example Implementation

Let’s look at how you could implement this middleware:

// dynamoServiceMiddleware.js
import { DynamoService } from 'path-to-dynamo-service'; // Make sure to adjust the path as needed

export const dynamoServiceMiddleware = () => {
let dynamoService;

return {
before: async (handler) => {
if (!dynamoService) {
dynamoService = new DynamoService();
}
handler.context.dynamoService = dynamoService;
}
};
};

Utilizing this middleware in your Lambda handler could look something like this:

import middy from '@middy/core';
import { BaseHandler } from 'path-to-your-base-handler'; // Ensure the path is correctly set
import { dynamoServiceMiddleware } from 'path-to-dynamo-service-middleware'; // Adjust the path accordingly

class MyLambdaHandler extends BaseHandler {
async main(event) {
const dynamoService = this.context.dynamoService;
const TableName = process.env.bacaSupportersTableName;
const { username } = event.pathParameters;

// Leverage the dynamoService for your database interactions
const response = await dynamoService.dynamodbGetHelper({ TableName, Key: { username } });

// Further process the response and prepare it for return
// ...
}

// Additional code if necessary
}

export const handler = new MyLambdaHandler().handler.use(dynamoServiceMiddleware());

This approach not only simplifies your Lambda functions by handling the DynamoService’s instantiation and management but also fosters code reusability and enhances maintainability. Adopting middleware for service initialization in serverless architectures can significantly streamline your development process, making your code cleaner and more efficient.

Benefits of using the Middleware

Adopting a middleware approach for service initialization, especially over repetitive copy-pasting, offers several significant advantages:

  1. Improved Maintainability: When you centralize the initialization of services like DynamoDB into middleware, you significantly reduce the complexity of your codebase. This means that any updates, upgrades, or bug fixes to the service instantiation process only need to be made in one place, rather than in every function where the service is used. This simplification makes your code easier to maintain and reduces the risk of errors during updates.
  2. Enhanced Reusability: Middleware allows you to create a reusable piece of code that can be applied across multiple Lambda functions. This not only saves time during the development process but also ensures consistency in how services are initialized and used across your application. You can easily share the same middleware logic across different projects or within the same project but different functions, promoting a DRY (Don’t Repeat Yourself) principle.
  3. Cleaner Code and Separation of Concerns: By handling service initialization in middleware, you can keep your Lambda handlers lean and focused solely on business logic. This separation of concerns makes your code more readable and easier to understand. Handlers no longer need to deal with the boilerplate of service instantiation, allowing developers to focus on the unique functionality of each Lambda function.
  4. Scalability: As your application grows, the middleware approach scales more gracefully. Adding new Lambda functions or services becomes a matter of plugging in the existing middleware, rather than copying and pasting initialization code. This scalability is crucial for large applications or those expecting to grow over time, as it simplifies the process of extending functionality.
  5. Increased Efficiency and Performance: Middleware can also improve the efficiency and performance of your serverless applications. By initializing services outside of your business logic, you can potentially reduce the initialization overhead for each Lambda invocation. For services that maintain a connection pool or have other initialization costs, doing this once per container rather than per invocation can lead to performance improvements, especially under high loads.

These advantages highlight the importance of adopting modern development practices like middleware for service initialization. It not only streamlines the development process but also results in more efficient, maintainable, and scalable serverless applications.

Conclusion

Adopting a middleware approach for service initialization in serverless architectures, particularly with AWS Lambda and DynamoDB, offers a compelling blend of benefits that align well with modern software development practices. My thoughts on this strategy underscore its value in promoting clean code, maintainability, scalability, and efficiency. Let’s delve into whether it’s worth investing additional time to research the right level of abstraction and why this might not yet be a universally standard practice.

Value of Researching the Right Abstraction

Investing time in finding the right level of abstraction for middleware in serverless functions is undoubtedly worthwhile. This effort pays dividends in long-term application maintainability and scalability. Middleware that correctly abstracts repeated logic (like service initialization) not only reduces code duplication but also centralizes logic for easier updates and debugging. The initial time spent in research and development can lead to significant time savings in the future, as well as a more robust and flexible application architecture.

Consideration as Standard Practice

Middleware usage for service initialization should be considered a best practice in serverless architecture for several reasons:

  • Encourages Clean Architecture: It enforces a separation of concerns, keeping business logic separate from boilerplate setup code.
  • Promotes Code Reusability and Maintainability: By abstracting common setup tasks, it simplifies codebases, making them easier to manage and extend.
  • Aligns with Principles of Serverless Computing: Maximizes the benefits of serverless architecture by focusing on the business logic, letting the cloud provider handle the rest.

Documentation and Adoption

The question of why such practices are not more widely documented or included in the templates of new projects is multifaceted:

  • Rapid Evolution of Cloud Services: The serverless landscape is rapidly evolving, with new features and best practices emerging regularly. Documentation and standard templates may lag behind the latest practices.
  • Variability of Use Cases: Middleware and abstraction layers are highly dependent on specific use cases. What works well for one application’s architecture might not be suitable for another, making it challenging to create one-size-fits-all templates.
  • Community and Knowledge Sharing: While cloud providers like AWS offer extensive documentation on their services, the dissemination of advanced patterns like this often relies on community sharing through blogs, forums, and conferences. As these practices become more mainstream, they’re more likely to be documented and adopted in official templates.

Given the advantages, it’s clear that spending time to understand and implement the right level of abstraction through middleware is beneficial. While it may not yet be universally recognized as standard practice due to the rapidly evolving nature of serverless computing and the diversity of application requirements, the trend is moving towards broader adoption. As the community continues to share knowledge and cloud providers evolve their documentation and templates, it’s likely that such practices will become more standardized and widely recommended. This evolution will help developers leverage serverless technologies more effectively, making the initial investment in learning these patterns well worth the effort.

Empower Your Tech Journey:

Explore a wealth of knowledge designed to elevate your tech projects and understanding. From safeguarding your applications to mastering serverless architecture, discover articles that resonate with your ambition.

New Projects or Consultancy

For new project collaborations or bespoke consultancy services, reach out directly and let’s transform your ideas into reality. Ready to take your project to the next level?

Protecting Routes

Advanced Serverless Techniques

Mastering Serverless Series

--

--