Skip to content

DataLoader layer for efficient fetching from DynamoDB

Notifications You must be signed in to change notification settings

acomagu/dynamodb-dataloader

Repository files navigation

DynamoDB DataLoader

This library provides a DataLoader layer for efficient fetching from DynamoDB by caching and batching.

Features

  • Batch Loading: Combines multiple queries into fewer network requests to DynamoDB(only for get operation).
  • Unified Caching: Caches are shared across get, query, and scan operations.
    • But this shared caching is effective only in limited scenarios, such as when entries previously fetched using query or scan are accessed again using get. Also, the feature does not function when only parts of records are retrieved.

Initializing the DataLoader

Define the schema for your tables, specifying each table's name and the attribute names that form the keys used in caching.

import { DynamodbDataLoader, TableSchema } from '@acomagu/dynamodb-dataloader';

const tableSchemas: TableSchema[] = [
  { tableName: "Users", keyAttributeNames: ["userId"] },
  { tableName: "Posts", keyAttributeNames: ["userId", "postId"] }, // PK and SK
]; // Used to enable cache sharing across query, scan, and get operations.

const options = {
  dynamodbClient: new DynamoDBClient({ /* AWS SDK configuration options */ }),
  getOptions: { /* BatchGet options */ },
};

const dynamodbDataLoader = new DynamodbDataLoader(tableSchemas, options); // All arguments are optional.

Propagate with Express Context

Following best practices of DataLoader, a new instance of DynamodbDataLoader should be created per client request.

This example inserts the dataLoader to the Request object in express middleware.

const app = express();

// Middleware to initialize DataLoader and store it in AsyncLocalStorage
app.use((req) => {
  req.dataLoader = new DynamodbDataLoader();
});

app.get('/user/:id', async (req, res) => {
  const item = await req.dataLoader.getter.load({
    TableName: "Users",
    Key: { userId: req.params.id },
  });
  res.send(item);
});

Store to AsyncLocalStorage

The another way to isolate DataLoader per client request is using AsyncLocalStorage.

const app = express();

const dynamodbDataLoaderStorage = new AsyncLocalStorage();

app.use((req, res, next) => {
  dynamodbDataLoaderStorage.run(new DynamodbDataLoader(), next);
});

app.get('/user/:id', async (req, res) => {
  const item = await dynamodbDataLoaderStorage.getStore()!.getter.load({
    TableName: "Users",
    Key: { userId: req.params.id },
  });
  res.send(item);
});

Usage

Fetching Data

Get Operation

Fetch data for a specific user ID from the "Users" table using the getter DataLoader:

const getUserRequest = {
  TableName: "Users",
  Key: { userId: "12345" }
};
const item = await dynamodbDataLoader.getter.load(getUserRequest);

Query Operation

Example of querying posts for a specific user:

const queryPostsRequest = {
  TableName: "Posts",
  KeyConditionExpression: "userId = :userId",
  ExpressionAttributeValues: {
    ":userId": "12345",
  },
};
const items = await dynamodbDataLoader.querier.load(queryPostsRequest);

Scan Operation

Scanning for items with a specific filter:

const scanRequest = {
  TableName: "Posts",
  FilterExpression: "contains(content, :content)",
  ExpressionAttributeValues: {
    ":content": "DynamoDB",
  },
};
const items = await dynamodbDataLoader.scanner.load(scanRequest);

API Documentation

Documentation

About

DataLoader layer for efficient fetching from DynamoDB

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published