Asjas / prisma-redis-middleware

Prisma Middleware for caching queries in Redis

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Thinking out loud about v2 of this library

Asjas opened this issue · comments

At the moment this Prisma middleware isn't very flexible and I feel that the caching API isn't that nice to use.

prismaClient.$use(
  createPrismaRedisCache({
    models: ["User", "Post"],
    cacheTime: 300,
    redis,
    excludeCacheMethods: ["findMany"],
  }),
);

I want to update the API to possibly look like this:

  • models: Array of objects. The key is the model to cache and the value is the time to cache it.
  • excludeModels: Array of strings. These models will NOT be cached.
  • redis: Optional. Falls back to using an in-memory LRU cache
  • cacheTime: Still an integer
  • excludeCacheMethods: Still an array of methods to ignore when caching

Example 1:

prismaClient.$use(
  createPrismaRedisCache({
    cacheTime: 300,
  }),
);

You can invoke the middleware while leaving the models out which will cause all models to be cached based on cacheTime.

And if you leave the redis instance out this should fall back to using an in-memory LRU cache. This should make it easier for someone to test this in development without needing to configure or install Redis.

Thoughts: In such a case it should print out a warning stating that an in-memory cache is being used and that you should pass a Redis instance to persist the cache.

Example 2:

prismaClient.$use(
  createPrismaRedisCache({
    models: [{ "User", 300 }, { "Post": 900 }],
    cacheTime: 300,
    redis,
    excludeCacheMethods: ["findMany"],
  }),
);

You can specify the caching time per model.

The global cacheTime middleware option is then used for any other Model that isn't explicitly specified in models.

You can then specify a value of 0 to cacheTime or leave the cacheTime option out to stop it from caching any explicit models you didn't specify.

Some thoughts about the models API

I don't know if I want the models to have an API that looks like this.

 models: [{ "User", 300 }, { "Post": 900 }],

Or like this:

 models: [{ model: "User", cacheTime: 300 }, { model: "Post", cacheTime: 900 }],

I like the 2nd one, but it also feels a slightly verbose... 🤔


Some other thoughts.

  1. Should I just ignore cacheTime if there is a models value (which will include a caching value per model)? Or should I use it for any model that isn't specified?

  2. Allowing someone to specify their own caching key per model...? yikes

  3. Allowing an option called excludeModels which could be an Array of strings which will NOT be cached?

The createPrismaRedisCache API I decided on is represented as this TypeScript type.

export type PrismaMutationAction =
  | "create"
  | "createMany"
  | "update"
  | "updateMany"
  | "upsert"
  | "delete"
  | "deleteMany"
  | "executeRawUnsafe";

export type PrismaQueryAction =
  | "findFirst"
  | "findUnique"
  | "findMany"
  | "aggregate"
  | "count"
  | "groupBy"
  | "queryRaw";

export type PrismaAction = PrismaMutationAction | PrismaQueryAction;

export type RedisMemoryOptions = {
  client: Redis.Redis;
  invalidation?: boolean | { referencesTTL?: number };
  log?: any;
};

export type MemoryStorageOptions = {
  size?: number;
  invalidation?: boolean;
  log?: any;
};

export type CreatePrismaRedisCache = {
  models?: [
    {
      model: string;
      primaryKey?: string;
      cacheTime?: number;
      excludeCacheMethods?: [PrismaQueryAction];
    },
  ];
  storage?:
    | {
        type: "redis";
        options?: RedisMemoryOptions;
      }
    | {
        type: "memory";
        options?: MemoryStorageOptions;
      };
  defaultCacheTime?: number;
  defaultExcludeCacheModels?: [string];
  defaultExcludeCacheMethods?: [PrismaQueryAction];
  onError?: () => void;
  onHit?: () => void;
  onMiss?: () => void;
  onDedupe?: () => void;
};