본문 바로가기

ChatGPT/AWS Serverless

[Serverless][DynamoDB] Express Session Store

반응형

Full code :
serverless-dynamodb-session-store

 

GitHub - gboysking/serverless-dynamodb-session-store

Contribute to gboysking/serverless-dynamodb-session-store development by creating an account on GitHub.

github.com


1. Introduction
2. Prerequisites
3. Implementation
4. Setting Up the Environment
5. Integration with Express.js
6. Troubleshooting
7. Deployment and Scaling
8. Conclusion


1. Introduction

In this blog post, we will explore the implementation of a custom session store for Express.js applications using Amazon DynamoDB. DynamoDB is a fully managed, fast, and flexible NoSQL database service provided by AWS. It is designed for applications that require consistent, single-digit millisecond latency at any scale.

Express.js is a popular web application framework for Node.js. It allows developers to easily build web applications and APIs with a minimal amount of boilerplate code. One of the key features of Express.js is its support for session management, which allows developers to maintain user-specific state across multiple HTTP requests.

To manage sessions in an Express.js application, we need a session store that can persist session data. By default, Express.js provides a simple in-memory session store, which is not suitable for production environments. For production use, it is important to choose a session store that is reliable, scalable, and easy to manage.

This is where our custom DynamoDB session store comes in. Our implementation of the session store provides a scalable and reliable solution for managing session data in an Express.js application using Amazon DynamoDB.

2. Prerequisites

Before we start implementing the DynamoDB session store, make sure you have the following prerequisites set up:

  1. Node.js: You should have Node.js installed on your machine. You can download the latest version from the official Node.js website. We recommend using the LTS (Long Term Support) version for better stability.
  1. AWS Account: You will need an AWS account to use Amazon DynamoDB. If you don't have one, you can sign up for a free tier account on the AWS website. The free tier provides limited access to various AWS services, including DynamoDB.
  1. AWS CLI: Install the AWS Command Line Interface (CLI) on your machine. The CLI allows you to interact with AWS services from the command line. Follow the official installation guide to set it up.
  1. AWS SDK for JavaScript: Our custom session store implementation will use the AWS SDK for JavaScript to interact with the DynamoDB service. You can install the SDK by running the following command:
npm install aws-sdk

 

  1. Express.js: You should have a basic understanding of how to create and configure an Express.js application. If you need a refresher, you can check out the official Express.js documentation.
  1. Express Session: We will use the 'express-session' package to handle sessions in our Express.js application. You can install it by running the following command:
npm install express-session

 

With these prerequisites in place, we are now ready to start implementing our custom DynamoDB session store

3. Implementation

In this section, we will walk through the process of implementing the custom DynamoDB session store. We will create a new class that extends the Store class from the express-session package and overrides its methods to interact with DynamoDB.

3.1. Constructor and Initialization

The constructor of the 'DynamoDBSessionStore' class accepts an options object that can include a table name and a client instance. If these values aren't provided, the constructor will use default values. During the initialization process, the constructor sets the initial state as 'INITIALIZING' and attempts to create the table if it does not already exist. Once the initialization is successful, the state is updated to 'INITIALIZED', and all pending promises are resolved.

constructor(options: TimeSeriesStatisticsManagerOptions) {
    if (options.client) {
        this.client = DynamoDBDocument.from(options.client);
    } else {
        this.client = DynamoDBDocument.from(new DynamoDBClient({}));
    }

    if (options.table) {
        this.table = options.table;
    } else {
        this.table = "statistics";
    }

    this.state = 'INITIALIZING';
    this.onReadyPromises = [];

    Promise.resolve()
        .then(() => {
            return this.createTableIfNotExists();
        })
        .then(() => {
            this.state = 'INITIALIZED';
            this.resolveReadyPromises();
        })
        .catch((error) => {
            this.state = "FAIL";
            this.rejectReadyPromises(error);
        });
}

 

3.2. onReady Method

The 'onReady' method returns a promise that resolves when the store is ready for use. This method is used internally by other methods to ensure the store is properly initialized before performing any operations.

onReady(): Promise<void> {
    return new Promise((resolve, reject) => {
        if (this.state === 'INITIALIZED') {
            resolve();
        } else if (this.state === 'FAIL') {
            reject();
        } else {
            this.onReadyPromises.push(resolve);
        }
    });
}

 

반응형

 

3.3. Managing Tables

The 'createTableIfNotExists' method checks if the specified table exists; if it doesn't, it will create the table. The 'waitUntilTableExists' method is utilized to wait until the table's status is active before proceeding with further operations.

async waitUntilTableExists(timeout: number = 6000): Promise<void> {
    const command: DescribeTableCommandInput = { TableName: this.table };
    const startTime = Date.now();
    const endTime = startTime + timeout;

    while (Date.now() < endTime) {
        try {
            let result = await this.client.send(new DescribeTableCommand(command));

            if (result.Table.TableStatus == TableStatus.ACTIVE) {
                return;
            } else if (
            result.Table.TableStatus == TableStatus.DELETING 
            || result.Table.TableStatus == TableStatus.INACCESSIBLE_ENCRYPTION_CREDENTIALS) {
                break;
            }

            await new Promise(resolve => setTimeout(resolve, 1000));
        } catch (e) {
            await new Promise(resolve => setTimeout(resolve, 1000));
        }
    }

    throw new Error(`Timed out waiting for table ${this.table} to exist`);
}

async createTableIfNotExists(): Promise<void> {
    try {
        await this.client.send(new DescribeTableCommand({ TableName: this.table }));
    } catch (error: any) {
        if (error.name === "ResourceNotFoundException") {
            const params = {
                AttributeDefinitions: [
                    { AttributeName: "topic_period", AttributeType: "S" },
                    { AttributeName: "time_partition", AttributeType: "N" }
                ],
                KeySchema: [
                    { AttributeName: "topic_period", KeyType: "HASH" },
                    { AttributeName: "time_partition", KeyType: "RANGE" }
                ],
                ProvisionedThroughput: {
                    ReadCapacityUnits: 5,
                    WriteCapacityUnits: 5
                },
                TableName: this.table
            };

            await this.client.send(new CreateTableCommand(params));

            // Wait until table is active
            await this.waitUntilTableExists();
        } else {
            console.error(
                "Error checking for the existence of the DynamoDB table:",
                error
            );
            throw error;
        }
    }
}

 

 

3.4. Managing Sessions

The 'DynamoDBSessionStore' class implements essential session management methods like 'get', 'set', 'destroy', 'length', 'touch', 'reap', and 'all'. These methods interact with DynamoDB to perform their respective operations on session data.

3.5. Error Handling

Throughout the class implementation, error handling ensures that errors are caught and reported accordingly. This feature allows the class to provide meaningful feedback to users and assists in troubleshooting potential issues.

With the implementation of the 'DynamoDBSessionStore' class complete, you can now integrate it into your Express.js application and leverage it to store and manage user sessions.

4. Setting Up the Environment

In this section, we will set up the environment for our Express.js application to use the custom DynamoDB session store that we implemented in the previous section.

  1. Install dependencies: Make sure you have the following dependencies installed in your project:
npm install express express-session @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

 

  1. Configure AWS SDK: If you haven't done so already, configure the AWS SDK with your AWS credentials. You can do this in various ways, such as using environment variables or a credentials file. For more information on how to set up your credentials, refer to the official AWS SDK documentation.
  1. Import the custom session store: In your main application file (e.g., 'app.ts' or 'app.js'), import the 'DynamoDBSessionStore' class:
import { DynamoDBSessionStore } from './dynamodb-session-store';

 

  1. Configure the session middleware: Configure the 'express-session' middleware to use the custom DynamoDB session store. Replace the 'store' property with an instance of 'DynamoDBSessionStore':
import express from 'express';
import session from 'express-session';

const app = express();

app.use(
  session({
    secret: 'your-secret-key',
    resave: false,
    saveUninitialized: true,
    store: new DynamoDBSessionStore({
      table: 'your-dynamodb-table-name', // Optional, defaults to 'sessions'
    }),
  })
);

// Your routes and other Express.js app configurations

 

That's it! Your Express.js application should now be set up to store session data in the specified DynamoDB table using the custom DynamoDB session store. Make sure you have the necessary AWS permissions to create, read, write, and delete items in the table.

5. Integration with Express.js

In this section, we will discuss how our custom DynamoDB session store seamlessly integrates with Express.js and the 'express-session' middleware.

  1. Adding the session middleware: As shown in the previous section, integrating our custom session store with Express.js is as simple as setting the 'store' property in the 'express-session' middleware configuration. This allows the middleware to delegate session management tasks (such as creating, updating, and deleting sessions) to our custom store.
app.use(
  session({
    secret: 'your-secret-key',
    resave: false,
    saveUninitialized: true,
    store: new DynamoDBSessionStore({
      table: 'your-dynamodb-table-name', // Optional, defaults to 'sessions'
    }),
  })
);

 

  1. Using sessions in your routes: Once the middleware is set up, you can use the 'req.session' object in your routes to store and retrieve session data. This data will be automatically managed by the custom DynamoDB session store.
app.get('/', (req, res) => {
  if (!req.session.views) {
    req.session.views = 0;
  }
  req.session.views++;

  res.send(`You have visited this page ${req.session.views} times`);
});

 

  1. Managing session lifecycle: The 'express-session' middleware and our custom DynamoDB session store handle the session lifecycle, including creation, updates, and expiration. This means you don't have to worry about manually managing session data or cleaning up expired sessions in your application.

With the integration complete, your Express.js application can now take advantage of the scalability, durability, and performance provided by Amazon DynamoDB for session management. This can be particularly useful for applications with a large number of concurrent users or distributed architectures where session data needs to be shared across multiple instances.

6. Troubleshooting

In this section, we will discuss some common issues that you may encounter while working with the custom DynamoDB session store and how to troubleshoot them.

  1. Session data not being saved or retrieved: If you find that your session data is not being saved or retrieved correctly, make sure that the 'express-session' middleware is correctly configured and that the 'store' property is set to an instance of the custom DynamoDB session store. Check for any errors in the console output, as these may indicate issues with the DynamoDB table or the AWS SDK configuration.
  1. Sessions not expiring as expected: If sessions are not expiring as expected, double-check the 'cookie' configuration in your 'express-session' middleware. Make sure that the 'maxAge' property is set correctly, and that the custom session store is correctly handling the 'expireTime' and 'expireTime_TTL' attributes. You may also want to verify that TTL is enabled on your DynamoDB table and that the attribute name is correctly set to expireTime_TTL.
  1. Errors connecting to DynamoDB: If you encounter errors when connecting to DynamoDB, such as "ResourceNotFoundException" or "ValidationException", ensure that your AWS SDK configuration (e.g., credentials, region, and endpoint) is correct. You may need to provide an appropriate IAM role with permissions to access the DynamoDB table or check the AWS SDK documentation for more information on configuring the SDK.
  1. 'Unexpected behavior or errors in your application: If you experience unexpected behavior or errors related to session management, it might be helpful to enable logging or debugging output in your application, the 'express-session' middleware, and the custom DynamoDB session store. This can help identify any issues with the implementation, configuration, or underlying services.

By addressing these common issues, you can ensure that your Express.js application takes full advantage of the custom DynamoDB session store for efficient and reliable session management.

7. Deployment and Scaling

In this section, we will discuss the deployment and scaling considerations when using the custom DynamoDB session store in your Express.js application.

  1. Deploying your Express.js application: When deploying your application, ensure that the necessary environment variables (e.g., AWS credentials, region, and DynamoDB table name) are properly configured. This will help the application connect to the correct DynamoDB table and manage session data as expected. Popular deployment options for Express.js applications include cloud platforms such as AWS Elastic Beanstalk, Heroku, or using containerization with Docker and Kubernetes.
  1. Scaling your DynamoDB table: As your application grows and requires more capacity for session data storage, you may need to scale your DynamoDB table. You can do this by adjusting the provisioned read and write capacity units or by enabling auto-scaling. Additionally, you can use global secondary indexes to optimize query performance, if needed.
  1. Handling regional latency: If your application serves users across different regions, you may need to consider the latency of connecting to the DynamoDB table. One option is to deploy your Express.js application and DynamoDB table in the same AWS region, minimizing latency. Alternatively, you can use DynamoDB global tables to replicate your session data across multiple regions, allowing for lower latency access to session data for users in different regions.
  1. Backup and disaster recovery: It's essential to have a backup and disaster recovery plan in place for your session data. You can use DynamoDB's built-in backup and restore functionality to create on-demand or continuous backups of your table. Additionally, consider implementing a disaster recovery plan that includes cross-region replication or utilizing AWS services such as AWS Backup.
  1. Monitoring and alerting: Keep an eye on your application's performance and the health of your DynamoDB table using AWS monitoring tools such as Amazon CloudWatch, AWS X-Ray, or third-party monitoring solutions. Set up appropriate alarms and notifications to be alerted of any potential issues or bottlenecks that may arise as your application scales.

 

8. Conclusion

In this blog post, we have discussed how to implement a custom DynamoDB session store for Express.js applications. We covered the prerequisites, the implementation process, setting up the environment, integrating with Express.js, and touched on testing, troubleshooting, deployment, and scaling.

By using DynamoDB as a session store, you can leverage its scalability, performance, and durability to manage your application's session data effectively. This approach helps improve the overall user experience and ensures that your application can handle a growing number of users and their session data.

As a final takeaway, always keep in mind the best practices when working with DynamoDB, such as optimizing queries, managing read and write capacity, and monitoring the table's health. By doing so, you can ensure the success of your Express.js application and its custom DynamoDB session store.

Happy coding!

 

 

This article was written with the help of ChatGPT.

반응형