DynamoDB-based session store for express-session, using the AWS SDK for JS v3, offering configurability for cost, performance, and reliability not found in other DynamoDB session stores.
DynamoDB is an excellent choice for session stores because it is a fully managed service that is highly available, durable, and can scale automatically (to nearly unlimited levels) to meet demand. DynamoDB reads will typically return in 1-3 ms if capacity is set correctly and the caller is located in the same region as the Table.
The package is available on npm as @pwrdrvr/dynamodb-session-store
npm i @pwrdrvr/dynamodb-session-store
import { DynamoDBStore } from '@pwrdrvr/dynamodb-session-store';
The following IAM permissions are required for the DynamoDB Table:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDynamoDBAccess",
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/dynamodb-session-store-test"
}
]
}
See the examples directory for more examples
import express from 'express';
import session from 'express-session';
import { DynamoDBStore } from '@pwrdrvr/dynamodb-session-store';
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const app = express();
const dynamoDBClient = new DynamoDBClient({});
app.use(
session({
store: new DynamoDBStore({
tableName: 'some-table',
dynamoDBClient,
touchAfter: 60 * 60, // 60 minutes in seconds
}),
secret: 'yeah-change-this',
cookie: {
maxAge: 14 * 24 * 60 * 60 * 1000, // 14 days in milliseconds
},
resave: false,
saveUninitialized: false,
}),
);
app.get('/login', (req, res) => {
console.log(`Session ID: ${req.session?.id}`);
req.session.user = 'test';
res.send('Logged in');
});
app.get('/*', (req, res) => {
res.status(200).send('Hello world');
});
app.listen(Number.parseInt(PORT, 10), () => {
console.log(`Example app listening on port ${port}`);
});
The following field types are fully supported by the DynamoDBStore:
stringnumberbooleanobjectThe following field types are partially supported by the DynamoDBStore:
DateDate object since it is not known which fields were originally Date objects vs date stringsDate objects to strings as well and also does not support automatic conversion back to Date objects since it serializes using JSON.stringify() and JSON.parse()After installing the package review the API Documentation for detailed on each configuration option.
Strongly Consistent Reads.touch() calls from express-sessionexpress-session would have made that a configurable option for every session store, but alas, it was rejected in favor of implementing the same thing 15+ timesexpires fieldDisclaimer: perform your own pricing calculation, monitor your costs during and after initial launch, and setup cost alerts to avoid unexpected charges.
Source: ./examples/express.ts
aws dynamodb create-table --table-name dynamodb-session-store-test --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUESTdynamodb-session-store-testidexpiresaws dynamodb update-time-to-live --table-name dynamodb-session-store-test --time-to-live-specification "Enabled=true, AttributeName=expires"npm run example:expressTABLE_NAME=my-table-name npm run example:expresshttp://localhost:3001/login in a browserSource: ./examples/cross-account.ts
This example has the DynamoDB in one account and the express app using an IAM role from another account to access the DynamoDB Table using temporary credentials from an STS AssumeRole call (neatly encapsulated by the AWS SDK for JS v3).
This example is more involved than the others as it requires setting up an IAM role that can be assumed by the app account.
Instructions for Cross-Account DynamoDB Table Example
Source: ./examples/other.ts
aws dynamodb create-table --table-name connect-dynamodb-test --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --billing-mode PAY_PER_REQUESTdynamodb-session-store-testidexpiresaws dynamodb update-time-to-live --table-name connect-dynamodb-test --time-to-live-specification "Enabled=true, AttributeName=expires"npm run example:expressTABLE_NAME=my-table-name npm run example:otherhttp://localhost:3001/login in a browser@pwrdrvr/dynamodb-session-storescan functionality that can make the DB completely unavailable if accidentally invokedtouch calls that update the TTL on every read, which can be very expensive (configurable)sess field to a Map on DynamoDB for easier querying vs dynamodb-connect which serializes to a Stringdynamodb-connect is easy, just set the prefix, hashKey, and table fields to the same values as dynamodb-connect - the get function will automatically deserialize the JSON sess field if foundexpress-session are provided@pwrdrvr/dynamodb-session-store DB Record{
"id": "123",
"sess": {
"cookie": {
"originalMaxAge": null,
"expires": null,
"httpOnly": true,
"path": "/"
},
"name": "paul"
},
"expires": 1621968000
}
dynamodb-connect DB Record{
"id": "123",
"sess": "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"name\":\"paul\"}",
"expires": 1621968000
}
Generated using TypeDoc