epic-stack icon indicating copy to clipboard operation
epic-stack copied to clipboard

Add documentation on how to use prisma client extensions for "enum"-like validation

Open kentcdodds opened this issue 2 years ago • 3 comments
trafficstars

SQLite doesn't support Enums, but sometimes you want a string field to only allow certain specific values. This is especially useful for simplifying typings throughout the application.

Prisma client extensions should allow for this. I would welcome a PR to the docs demonstrating how to do this.

kentcdodds avatar May 13 '23 15:05 kentcdodds

As stated here https://github.com/prisma/prisma/issues/2219#issuecomment-1506975020 this feature its not being developed in the future, maybe never, neither with check constraints or with any other method. Here is another documentation explainig why they are opting-ing strictly for db-native features. https://github.com/prisma/specs/issues/330#issue-525152162 Some other native features are also not in the roadmap from the time beign e.g. JSON field type support: https://github.com/prisma/prisma/issues/3786

Pablets avatar May 31 '23 19:05 Pablets

As noted, I'm talking about using Prisma client extensions feature which does mean this wouldn't be a database-level enhancement, but just one for the client which is sufficient for us.

kentcdodds avatar May 31 '23 20:05 kentcdodds

For people looking for it.

You can make client extensions like this:

import { z } from "zod";
import { Prisma } from "@prisma/client";

const schema = z.object({
  slug: z
    .string()
    .max(100)
    .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
  name: z.string().max(100),
  description: z.string().max(1000),
  price: z
    .instanceof(Prisma.Decimal)
    .refine((price) => price.gte("0.01") && price.lt("1000000.00")),
}) satisfies z.Schema<Prisma.ProductUncheckedCreateInput>;

export const ProductValidation = Prisma.defineExtension({
  query: {
    product: {
      create({ args, query }) {
        args.data = schema.parse(args.data);
        return query(args);
      },
      update({ args, query }) {
        args.data = schema.partial().parse(args.data);
        return query(args);
      },
      updateMany({ args, query }) {
        args.data = schema.partial().parse(args.data);
        return query(args);
      },
      upsert({ args, query }) {
        args.create = schema.parse(args.create);
        args.update = schema.partial().parse(args.update);
        return query(args);
      },
    },
  },
});

And then use it like this:

import { Prisma, PrismaClient } from "@prisma/client";
import { ProductValidation } from "./models/product";
import { ReviewValidation } from "./models/review";

const prisma = new PrismaClient()
  .$extends(ProductValidation)
  .$extends(ReviewValidation);

See: https://github.com/prisma/prisma-client-extensions/blob/main/input-validation/README.md

kasperpeulen avatar Jun 10 '23 13:06 kasperpeulen

Thanks @L-Steinmacher!

kentcdodds avatar Jun 27 '23 18:06 kentcdodds