Prisma + Zod, where to validate uniqueness

Say I have a Prisma schema with multiple unique fields (for example an ID and a URL), where do I validate the uniqueness of these fields on the server? I could use an async ZOD refinement, which makes it easy to provide a custom error message etc to the user but this is more costly DB wise. I could also just let Prisma throw, but formatting a custom error message there is a bit of a pain. It's possible to check the type of the error and implement custom logic based on the code, but the field on which the error is thrown is passed through meta.target which doesn't seem to be properly typed. This results in some quite ugly code checking the code of the error first, checking if there is a meta , a target and checking if that is an array. https://www.prisma.io/docs/reference/api-reference/error-reference#prismaclientknownrequesterror How do you guys handle this?
Prisma
Prisma error message reference (Reference)
Prisma Client, Migrate, Introspection error message reference
3 Replies
JulieCezar
JulieCezar16mo ago
When this happens, you DB will throw the error and prisma will catch it... that's why you need to wrap all prisma calls in try{}catch{} I forgot how the error object looks exactly but I know that each such error has a specific code in the DB In my case, with PostgreSQL, when you add a duplicate id it says error P2001
janglad
jangladOP16mo ago
Yup this is true and I'm doing this but I'm facing some challenges formatting that into a nice message for the user @juliecezar Specifically getting the name of the field(s) that caused the error, cause it could be multiple if a model has multiple fields that should be unique
kotyan
kotyan15mo ago
I've created a utility function validateUniqueConstraint to handle unique constraint errors from Prisma. It takes in a field name, error message, and a function that triggers a Prisma operation. If a unique constraint error occurs on the specified field, it throws a user-friendly TRPCError with the provided error message. Then I handle this error using enum on the frontend.
const validateUniqueConstraint = async ({
fieldName,
errorMessage,
func,
}: {
fieldName: string;
errorMessage: string;
func: Function;
}) => {
try {
return await func();
} catch (e) {
if (
e instanceof PrismaClientKnownRequestError &&
e.code === "P2002" &&
(e.meta?.target as string[])?.includes(fieldName)
) {
throw new TRPCError({
code: "CONFLICT",
message: errorMessage,
});
}
}
};

enum ErrorMessages {
FIELD_ALREADY_EXISTS = "Field already exists",
}

const result = await validateUniqueConstraint({
fieldName: "name",
errorMessage: ErrorMessages.FIELD_ALREADY_EXISTS,
func: () =>
prisma.entity.create({
data: {
field: "value",
},
}),
});
const validateUniqueConstraint = async ({
fieldName,
errorMessage,
func,
}: {
fieldName: string;
errorMessage: string;
func: Function;
}) => {
try {
return await func();
} catch (e) {
if (
e instanceof PrismaClientKnownRequestError &&
e.code === "P2002" &&
(e.meta?.target as string[])?.includes(fieldName)
) {
throw new TRPCError({
code: "CONFLICT",
message: errorMessage,
});
}
}
};

enum ErrorMessages {
FIELD_ALREADY_EXISTS = "Field already exists",
}

const result = await validateUniqueConstraint({
fieldName: "name",
errorMessage: ErrorMessages.FIELD_ALREADY_EXISTS,
func: () =>
prisma.entity.create({
data: {
field: "value",
},
}),
});
frontend:
useEffect(() => {
if (error && error.message === CommunityErrorMessages.NAME_ALREADY_EXISTS) {
form.setError("name", {
type: "manual",
message: CommunityErrorMessages.NAME_ALREADY_EXISTS,
});
}
}, [error, form]);
useEffect(() => {
if (error && error.message === CommunityErrorMessages.NAME_ALREADY_EXISTS) {
form.setError("name", {
type: "manual",
message: CommunityErrorMessages.NAME_ALREADY_EXISTS,
});
}
}, [error, form]);
Want results from more Discord servers?
Add your server