Awesome
prisma-redis-middleware
This is a Prisma middleware used for caching and storing of Prisma queries in Redis (uses an in-memory LRU cache as fallback storage).
Uses async-cache-dedupe.
Features
- Cache Invalidation
- Supports custom cache keys
- Cache persistance with Redis (uses an in-memory LRU cache as fallback)
- Caching multiple Prisma models each with a specific cache time
- Excluding certain Prisma models from being cached
- Excluding certain Prisma queries from being cached across all models
Supported Node.js versions
The latest versions of the following Node.js versions are tested and supported.
- 16
- 18
Default Cached Methods
Here is a list of all the Prisma methods that are currently cached by default in prisma-redis-middleware
.
- findUnique
- findUniqueOrThrow
- findFirst
- findFirstOrThrow
- findMany
- count
- aggregate
- groupBy
- findRaw
- aggregateRaw
queryRaw
is not cached as it's executed against the Prisma db itself and not a model. This Prisma middleware is used
for caching queries based on the models that they are executed against.
Quick Start
Install the package using npm
:
npm i --save-exact prisma-redis-middleware
You will also need to install and configure an external dependency for Redis
(for example: ioredis
or one that uses
a similar API) if you don't already have a Redis Client in your project.
npm i --save-exact ioredis @types/ioredis
Code Example (ESM / Import)
import Prisma from "prisma";
import { PrismaClient } from "@prisma/client";
import { createPrismaRedisCache } from "prisma-redis-middleware";
import Redis from "ioredis";
const redis = new Redis(); // Uses default options for Redis connection
const prisma = new PrismaClient();
const cacheMiddleware: Prisma.Middleware = createPrismaRedisCache({
models: [
{ model: "User", excludeMethods: ["findMany"] },
{ model: "Post", cacheTime: 180, cacheKey: "article" },
],
storage: { type: "redis", options: { client: redis, invalidation: { referencesTTL: 300 }, log: console } },
cacheTime: 300,
excludeModels: ["Product", "Cart"],
excludeMethods: ["count", "groupBy"],
onHit: (key) => {
console.log("hit", key);
},
onMiss: (key) => {
console.log("miss", key);
},
onError: (key) => {
console.log("error", key);
},
});
prisma.$use(cacheMiddleware);
Code Example (Common JS / Require)
const Prisma = require("prisma");
const { PrismaClient } = require("@prisma/client");
const { createPrismaRedisCache } = require("prisma-redis-middleware");
const prisma = new PrismaClient();
const cacheMiddleware: Prisma.Middleware = createPrismaRedisCache({
models: [
{ model: "User", cacheTime: 60 },
{ model: "Post", cacheTime: 180 },
],
storage: { type: "memory", options: { invalidation: true, log: console } },
cacheTime: 300,
onHit: (key) => {
console.log("hit", key);
},
onMiss: (key) => {
console.log("miss", key);
},
onError: (key) => {
console.log("error", key);
},
});
prisma.$use(cacheMiddleware);
API
createPrismaRedisCache(opts)
Options:
-
onDedupe
: (optional) a function that is called every time a query is deduped. -
onError
: (optional) a function that is called every time there is a cache error. -
onHit
: (optional) a function that is called every time there is a hit in the cache. -
onMiss
: (optional) a function that is called every time the result is not in the cache. -
cacheTime
: (optional) (number) the default time (in seconds) to use for models that don't have acacheTime
value set. Default is 0. -
excludeModels
: (optional) (string) an array of models to exclude from being cached. -
excludeMethods
: (optional) (string) an array of Prisma methods to exclude from being cached for all models. -
models
: (optional) an array of Prisma models. Models options are:-
model
: (required) string. -
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:
createPrismaRedisCache({ models: [ { model: "User", cacheTime: 60, invalidateRelated: ["Post"] }, { model: "Post", cacheKey: "article", excludeMethods: ["findFirst"] }, ], });
-
-
storage
: (optional) the storage options; default is{ type: "memory" }
. Storage options are:-
type
:memory
(default) orredis
-
options
: by storage type-
for
memory
typesize
: (optional) maximum number of items to store in the cache. Default is1024
.invalidation
: (optional) enable invalidation. Default is disabled.log
: (optional) logger instancepino
compatible, orconsole
, default is disabled.
Example:
createPrismaRedisCache({ storage: { type: "memory", options: { size: 2048 }, log: console }, });
-
for
redis
typeclient
: a redis client instance, mandatory. Should be anioredis
client or compatible.invalidation
: (optional) enable invalidation. Default is disabled.invalidation.referencesTTL
: (optional) references TTL in seconds, it means how long the references are alive; it should be set at the maximum of all the caches ttl.log
: (optional) logger instancepino
compatible, orconsole
, default is disabled.
Example
const redis = new Redis(); createPrismaRedisCache({ storage: { type: "redis", options: { client: redis, invalidation: { referencesTTL: 60 }, log: console }, }, });
-
-
-
transformer
: (optional) the transformer to used to serialize and deserialize the cache entries. It must be an object with the following methods:-
serialize
: a function that receives the result of the original function and returns a serializable object. -
deserialize
: a function that receives the serialized object and returns the original result. -
Default is
undefined
, so the default transformer is used.Example
import superjson from "superjson"; createPrismaRedisCache({ transformer: { serialize: (result) => superjson.serialize(result), deserialize: (serialized) => superjson.deserialize(serialized), }, });
-
Debugging
You can pass functions for onMiss
, onHit
, onError
and onDedupe
to createPrismaRedisCache
which can then be
used to debug whether a Prisma query is being cached or not.
You can also pass a custom log
(pino or console) to the storage
option and async-cache-dedupe
will print debug
info as it queries, sets, expires and invalidates the cache. Note that the log
option can print out very verbose
output.