Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/Asjas/prisma-redis-middleware

Prisma Middleware for caching queries in Redis
https://github.com/Asjas/prisma-redis-middleware

caching prisma prisma-middleware redis

Last synced: 29 days ago
JSON representation

Prisma Middleware for caching queries in Redis

Awesome Lists containing this project

README

        

# `prisma-redis-middleware`

[![License: Hippocratic 3.0](https://img.shields.io/badge/License-Hippocratic_3.0-lightgrey.svg)](https://firstdonoharm.dev)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![npm version](https://badge.fury.io/js/prisma-redis-middleware.svg)](https://badge.fury.io/js/prisma-redis-middleware)
[![codecov](https://codecov.io/gh/Asjas/prisma-redis-middleware/branch/main/graph/badge.svg?token=6F6DDOSRK8)](https://codecov.io/gh/Asjas/prisma-redis-middleware)
[![Main WorkFlow](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/main.yml/badge.svg)](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/main.yml)
[![CodeQL WorkFlow](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/codeql-analysis.yml)
[![Library code size](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/size.yml/badge.svg)](https://github.com/Asjas/prisma-redis-middleware/actions/workflows/size.yml)

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](https://github.com/mcollina/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`:

```sh
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._

```sh
npm i --save-exact ioredis @types/ioredis
```

## Code Example (ESM / Import)

```mjs
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)

```js
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 a `cacheTime` 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:

```js
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) or `redis`
- `options`: by storage type

- for `memory` type

- `size`: (optional) maximum number of items to store in the cache. Default is `1024`.
- `invalidation`: (optional) enable invalidation. Default is disabled.
- `log`: (optional) logger instance `pino` compatible, or `console`, default is disabled.

Example:

```js
createPrismaRedisCache({
storage: { type: "memory", options: { size: 2048 }, log: console },
});
```

- for `redis` type

- `client`: a redis client instance, mandatory. Should be an `ioredis` 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 instance `pino` compatible, or `console`, default is disabled.

Example

```js
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

```js
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.