diff --git a/README.md b/README.md index a53881f..38a8f54 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ const fastify = Fastify().register(dynamodbCache, { tableName: "fastify-dynamodb-cache", // DynamoDB table name defaultTTLSeconds: 30, // Default TTL (seconds), which would be used if no TTL is specified on the endpoint. disableCache: true, // Optional! If you want to disable caching from being set on endpoints, you can set this to true. Set it to false or leave it empty to enable cache. + passthroughQueryParam: "_t", // Optional! If you want to define a query parameter for all endpoints, which will be used to bypass the cache (will set 'x-cache: ignored'). }); fastify.get( diff --git a/src/helpers/hasQueryParams.ts b/src/helpers/hasQueryParams.ts new file mode 100644 index 0000000..2a40b68 --- /dev/null +++ b/src/helpers/hasQueryParams.ts @@ -0,0 +1,11 @@ +export const hasQueryParam = ( + query: unknown, + key: string +): query is Record => { + return ( + typeof query === "object" && + query !== null && + !Array.isArray(query) && + key in query + ); +}; diff --git a/src/hooks/onRequest.ts b/src/hooks/onRequest.ts index 011e90a..7ea3ad7 100644 --- a/src/hooks/onRequest.ts +++ b/src/hooks/onRequest.ts @@ -1,15 +1,18 @@ import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb"; import { FastifyRequest, FastifyReply } from "fastify"; import { onRequestAsyncHookHandler } from "fastify/types/hooks"; +import { hasQueryParam } from "../helpers/hasQueryParams"; interface CreateOnRequestHookOptions { dynamoClient: DynamoDBClient; tableName: string; + passthroughQueryParam?: string; } export const createOnRequestHook = ({ dynamoClient, tableName, + passthroughQueryParam, }: CreateOnRequestHookOptions) => { const onRequestHook: onRequestAsyncHookHandler = async ( request: FastifyRequest, @@ -22,6 +25,14 @@ export const createOnRequestHook = ({ }, }); + if ( + passthroughQueryParam && + hasQueryParam(request.query, passthroughQueryParam) + ) { + reply.header("x-cache", "ignored"); + return; + } + try { const { Item } = await dynamoClient.send(command); diff --git a/src/index.ts b/src/index.ts index 7cc5a69..7baf2f1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,15 +4,50 @@ import fastifyPlugin from "fastify-plugin"; import { createOnRequestHook } from "./hooks/onRequest"; import { createOnSendHook } from "./hooks/onSend"; -export interface PluginOptions { +/** + * Plugin options for fastify-aws-dynamodb-cache. + * + * @property {string} dynamoDbRegion - AWS region for DynamoDB client configuration. + * @property {string} [dynamoDbAddress] - Optional custom endpoint (useful for local development/testing). + * @property {string} tableName - DynamoDB table name used for storing cache entries. + * @property {number} defaultTTLSeconds - Global default TTL (in seconds) for all cache entries. + * @property {boolean} [disableCache=false] - If true, disables caching plugin-wide. + * @property {string} [passthroughQueryParam] - If defined, this query parameter will bypass cache when present in a request. + */ +export interface DynamodbCachePluginOptions { dynamoDbRegion: string; dynamoDbAddress?: string; tableName: string; defaultTTLSeconds: number; disableCache?: boolean; + passthroughQueryParam?: string; } -export const dynamodbCache: FastifyPluginAsync = ( +/** + * Fastify plugin for caching responses in DynamoDB. + * + * Adds support for per-route caching via a shared DynamoDB table. + * + * Use route-level `config.cache` to control TTL and caching behavior: + * + * @example + * ```ts + * fastify.get('/my-route', { + * config: { + * cache: { + * cacheEnabled: true, + * ttlSeconds: 120 + * } + * } + * }, async (req, reply) => { + * return { hello: 'world' }; + * }); + * ``` + * + * @param fastify - Fastify instance + * @param opts - Plugin options for DynamoDB caching + */ +export const dynamodbCache: FastifyPluginAsync = ( fastify, opts ) => { @@ -31,6 +66,7 @@ export const dynamodbCache: FastifyPluginAsync = ( const onRequestHook = createOnRequestHook({ dynamoClient, tableName: opts.tableName, + passthroughQueryParam: opts.passthroughQueryParam, }); const onSendHook = createOnSendHook({ @@ -63,8 +99,17 @@ export const dynamodbCache: FastifyPluginAsync = ( declare module "fastify" { interface FastifyContextConfig { + /** + * Route-specific cache configuration. + */ cache?: { + /** + * Enable or disable cache for this route. Defaults to false. + */ cacheEnabled?: boolean; + /** + * TTL in seconds for the cached response. Overrides global defaultTTLSeconds if set. + */ ttlSeconds?: number; }; }