Often, your data model will have a unique constraint to prevent duplicate records.
For example:
model Character {
id Int @id @default(autoincrement())
name String @unique
appearsIn Episode[]
}
A Character
has to have a unique name. No two characters can be named the same.
If you try to add a new Character with the same name – or update an existing Character with a name that already exists – then your database and Prisma will prevent the data change from happen and raise an error.
Unless you handle this error, the RedwoodJS GraphQL API will mask the error with the Something went wrong
message and a stack trace will be shown in the logs.
Alternatively, you could check the existence of the record or use the [validateUniqueness Service Validation[(Services | RedwoodJS Docs), but that requires two database transactions per request.
What if you could simple more gracefully handle the error?
Prisma defines many error codes and one of them P2002
is for an error when the Unique constraint failed
.
Therefore, we can try/catch the error, check to see if it due to a unique constraint, and throw a RedwoodError
that will send a more friendly message back through the GraphQL response.
The following code for the characters
service demonstrates how to catch and handle the P2002
unique constraint failed error.
import type { QueryResolvers, MutationResolvers } from 'types/graphql'
import { Prisma } from '@prisma/client'
import { RedwoodError } from '@redwoodjs/api'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'
export const createCharacter: MutationResolvers['createCharacter'] = async ({
input,
}) => {
try {
return await db.character.create({
data: input,
})
} catch (e) {
logger.error(e, 'Error creating character')
if (e instanceof Prisma.PrismaClientKnownRequestError) {
// P2022: Unique constraint failed
// Prisma error codes: https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes
if (e.code === 'P2002') {
logger.error('The character already exists', e)
throw new RedwoodError('The character already exists')
}
}
throw e
}
}
export const updateCharacter: MutationResolvers['updateCharacter'] = async ({
id,
input,
}) => {
try {
return await db.character.update({
data: input,
where: { id },
})
} catch (e) {
logger.error(e, 'Error updating character')
if (e instanceof Prisma.PrismaClientKnownRequestError) {
// P2022: Unique constraint failed
// Prisma error codes: https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes
if (e.code === 'P2002') {
logger.error('The character already exists', e)
throw new RedwoodError('The character already exists')
}
}
throw e
}
}
Note: It is important here that your service is async/await. Redwood services generate without this, but is needed to return the error properly.
Now you have given the user a better experience and can inform them why the create or update failed.
To explore more Prisma error code, see: error codes in the Prisma reference documentation.
Next
Maybe one can parse the error info
api | 11:47:35 🐛 graphql-server GraphQL execution started: CreateCharacterMutation
api | 11:47:35 🚨 Error creating character
api |
api | 🚨 PrismaClientKnownRequestError Info
api |
api | {
api | "code": "P2002",
api | "clientVersion": "4.3.1",
api | "meta": {
api | "target": [
api | "name"
api | ]
api | }
api | }
To enrich the message with the field names and/or values that violated the constraint?