Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
feat: Invalidate related models when mutating
Browse files Browse the repository at this point in the history
  • Loading branch information
pobidowski committed Dec 14, 2022
1 parent 9f59909 commit 23e718f
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,6 @@ dist
# Ignore Prisma SQLite database
dev.db
dev.db-journal

# IntelliJ IDEA project files
.idea
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,14 @@ Options:
- `cacheKey`: (optional) string. Default is the model value.
- `cacheTime`: (optional) number (in seconds).
- `excludeMethods`: (optional) (string) an array of Prisma methods to exclude from being cached for this model.
- `invalidateRelated`: (optional) (string) an array of Prisma models to invalidate when mutating this model.

Example:

```js
createPrismaRedisCache({
models: [
{ model: "User", cacheTime: 60 },
{ model: "User", cacheTime: 60, invalidateRelated: ["Post"] },
{ model: "Post", cacheKey: "article", excludeMethods: ["findFirst"] },
],
});
Expand Down
18 changes: 13 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ export const createPrismaRedisCache = ({

// For each defined model in `models` we check if they defined any caching methods to be excluded
const excludedCacheMethodsInModels = models?.find(({ model, excludeMethods }) => {
if (model === params.model && excludeMethods?.length) {
return true;
}

return false;
return model === params.model && excludeMethods?.length;
});

// Do not define a cache function for any Prisma model if it already exists
Expand Down Expand Up @@ -137,6 +133,18 @@ export const createPrismaRedisCache = ({
// If we successfully executed the Mutation we clear and invalidate the cache for the Prisma model
if (defaultMutationMethods.includes(params.action as PrismaMutationAction)) {
await cache.invalidateAll(`*${params.model}~*`);

await Promise.all(
(models || [])
.filter(({ model }) => model === params.model)
.map(async ({ invalidateRelated }) => {
if (invalidateRelated) {
await Promise.all(
invalidateRelated.map(async (relatedModel) => cache.invalidateAll(`*${relatedModel}~*`)),
);
}
}),
);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ export type TtlFunction = (data: any) => number;

export type CreatePrismaRedisCache = {
models?: {
model: string;
model: string | Prisma.ModelName;
cacheKey?: string;
cacheTime?: number | TtlFunction;
excludeMethods?: PrismaQueryAction[];
invalidateRelated?: string[] | Prisma.ModelName[];
}[];
storage?:
| {
Expand Down
57 changes: 57 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,63 @@ describe.each<{
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);
});

test("should invalidate a Prisma model cache and related Prisma models after data mutation", async () => {
const middleware = createPrismaRedisCache({
models: [
{
model: model1,
invalidateRelated: [model2],
},
],
storage: { type: "redis", options: { client: redis, invalidation: true } },
cacheTime,
});

// Run a "fake" User Prisma query
await middleware(
{
args,
action: action1,
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Run a "fake" Post Prisma query
await middleware(
{
args,
action: action2,
model: model2,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if data exists in the cache
expect(JSON.parse((await redis.get(cacheKey1)) as string)).toMatchObject(dbValue);
expect(JSON.parse((await redis.get(cacheKey2)) as string)).toMatchObject(dbValue);

// Run a "fake" User Prisma mutation
await middleware(
{
args,
action: "create",
model: model1,
dataPath: [],
runInTransaction: false,
},
next,
);

// Test if the cache was invalidated and cleared properly
assert.equal(JSON.parse((await redis.get(cacheKey1)) as string), null);
assert.equal(JSON.parse((await redis.get(cacheKey2)) as string), null);
});

test("should not invalidate a Prisma model if cache method is excluded", async () => {
const middleware = createPrismaRedisCache({
storage: { type: "redis", options: { client: redis, invalidation: true } },
Expand Down

0 comments on commit 23e718f

Please sign in to comment.