Introduction
Amazon Web Services (AWS) powers the internet, each of outage results in millions of loss potential. The AWS SDK is what enables developers to utilize the various AWS services.
The AWS SDK JS is on its third version, with the latest version introducing a middleware stack enabling developers to customize the SDK behavior via the middleware.
Common usages for the middleware are: authentication, rate limiting, circuit breaking, request deduplication, shadow testing, tracing and request routing.
The AWS SDK v3 middleware stack
Every SDK client owns a middlewareStack. When you call client.send(command) the SDK runs the input through an ordered chain of middleware before it ever reaches the HTTP handler. Each middleware can inspect, modify, short-circuit, or post-process the request and response.
AWS SDK Caching
Caching is typically implemented on a per method basis via:
const map: Map<String, Response> = new Map()
const getData = async (id: String) => {
if(!map.has(id)) {
const response = await client.send({ id });
map.set(id, response);
}
return map.get(id);
}
This leads to a ton of bloat everywhere and actually has a bug for duplicate in-flight requests. The solution is to move it to a middleware.
aws-sdk-cache-middleware
aws-sdk-cache-middleware is a library that allows you to easily move the caching step into AWS SDK JS’s middleware stack.
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import type { GetObjectCommandOutput } from "@aws-sdk/client-s3";
import { createCachingMiddleware } from "aws-sdk-cache-middleware";
import type { CacheInputExtension } from "aws-sdk-cache-middleware";
const cache = new Map<string, GetObjectCommandOutput>();
const s3 = new S3Client({ region: "us-east-1" });
s3.middlewareStack.use(
createCachingMiddleware<GetObjectCommandOutput>({
store: {
get: (key) => cache.get(key),
set: (key, value) => { cache.set(key, value); },
},
onHit: (key) => console.log(`[HIT] ${key}`),
onMiss: (key) => console.log(`[MISS] ${key}`),
})
);
Add cacheKey to opt in:
const result = await s3.send(
new GetObjectCommand({
Bucket: "my-bucket",
Key: "config.json",
cacheKey: "s3:my-bucket/config.json",
} as Parameters<typeof GetObjectCommand>[0] & CacheInputExtension)
);
To avoid the cast, use CachableCommand — a constructor type helper that adds cacheKey to an existing command class:
import type { CachableCommand } from "cache_middleware";
type CachableGetObject = CachableCommand<GetObjectCommandInput, GetObjectCommandOutput>;
const CachableGetObjectCommand = GetObjectCommand as unknown as CachableGetObject;
const result = await s3.send(
new CachableGetObjectCommand({
Bucket: "my-bucket",
Key: "config.json",
cacheKey: "s3:my-bucket/config.json",
})
);
It’s that simple to add your favorite caching solution to all your aws-sdk calls.
Full source: cache_middleware.