Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"Nuxt",
"Sveltekit",
"Pothos",
"backoff",
"Replibyte",
"Snaplet"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ enum Role {
```

</TopBlock>

## Going further

You can also use [Prisma Client extensions](/concepts/components/prisma-client/client-extensions) to log the time it takes to perform a query. A functional example can be found in [this GitHub repository](https://github.com/prisma/prisma-client-extensions/tree/main/query-logging).
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,149 @@ metaDescription: 'This page explains how to add custom validation to Prisma Clie

<TopBlock>

Prisma Client has type-safety and run-time type validation but does not include validation for user input.
You can add runtime validation for your user input for Prisma Client queries in one of the following ways:

This means you can use any validation library you'd like. The Node.js ecosystem offers a number of high-quality, easy-to-use validation libraries to choose from including: [joi](https://github.com/sideway/joi), [validator.js](https://github.com/validatorjs/validator.js), [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod) and [Superstruct](https://github.com/ianstormtaylor/superstruct).
- [Prisma Client extensions](/concepts/components/prisma-client/client-extensions)
- A custom function

You can use any validation library you'd like. The Node.js ecosystem offers a number of high-quality, easy-to-use validation libraries to choose from including: [joi](https://github.com/sideway/joi), [validator.js](https://github.com/validatorjs/validator.js), [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod) and [Superstruct](https://github.com/ianstormtaylor/superstruct).

</TopBlock>

## Custom Signup Validation
## Input validation with Prisma Client extensions

> Prisma Client extensions are currently in [Preview](/about/prisma/releases#preview).

This example adds runtime validation when creating and updating values using a Zod schema to check that the data passed to Prisma Client is valid.

<Admonition type="warning">

Query extensions do not currently work for nested operations. In this example, validations are only run on the top level data object passed to methods such as `prisma.product.create()`. Validations implemented this way do not automatically run for [nested writes](https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes).

</Admonition>

<TabbedContent tabs={[ <FileWithIcon text="Prisma Client extension" icon="file"/>, <FileWithIcon text="Prisma schema" icon="file"/>]}>

<tab>

```ts copy
import { PrismaClient, Prisma } from "@prisma/client";
import { z } from 'zod'

/**
* Zod schema
*/
export const ProductCreateInput = 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>;

/**
* Prisma Client Extension
*/
const prisma = new PrismaClient().$extends({
query: {
product: {
create({ args, query }) {
args.data = ProductCreateInput.parse(args.data);
return query(args);
},
update({ args, query }) {
args.data = ProductCreateInput.partial().parse(args.data);
return query(args);
},
updateMany({ args, query }) {
args.data = ProductCreateInput.partial().parse(args.data);
return query(args);
},
upsert({ args, query }) {
args.create = ProductCreateInput.parse(args.create);
args.update = ProductCreateInput.partial().parse(args.update);
return query(args);
},
},
},
});

async function main (){
/**
* Example usage
*/
// Valid product
const product = await prisma.product.create({
data: {
slug: "example-product",
name: "Example Product",
description: "Lorem ipsum dolor sit amet",
price: new Prisma.Decimal("10.95"),
},
});

// Invalid product
try {
await prisma.product.create({
data: {
slug: "invalid-product",
name: "Invalid Product",
description: "Lorem ipsum dolor sit amet",
price: new Prisma.Decimal("-1.00"),
},
});
} catch (err: any) {
console.log(err?.cause?.issues);
}
}

main()
```

</tab>

<tab>

```prisma copy
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

generator client {
provider = "prisma-client-js"
previewFeatures = ["clientExtensions"]
}

model Product {
id String @id @default(cuid())
slug String
name String
description String
price Decimal
reviews Review[]
}

model Review {
id String @id @default(cuid())
body String
stars Int
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
productId String
}
```

</tab>

</TabbedContent>

The above example uses a Zod schema to validate and parse data provided in a query at runtime before a record is written to the database.

## Input validation with a custom validation function

Here's an example using [Superstruct](https://github.com/ianstormtaylor/superstruct) to validate that the data needed to signup a new user is correct:

Expand Down Expand Up @@ -50,7 +186,8 @@ async function signup(input: Signup): Promise<User> {

The example above shows how you can create a custom type-safe `signup` function that ensures the input is valid before creating a user.

## Going Further
## Going further

- Learn how you can use [Prisma Client extensions](/concepts/components/prisma-client/client-extensions) to add input validation for your queries — [example](https://github.com/prisma/prisma-client-extensions/tree/main/input-validation).
- Learn how you can organize your code better by moving the `signup` function into [a custom model](/concepts/components/prisma-client/custom-models).
- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/3528) to bake user validation into Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case!
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,106 @@ Computed fields allow you to derive a new field based on existing data. A common

</TopBlock>

## Deriving a Full Name from a First and Last Name
## Using a Prisma Client extension

Prisma Client does not yet natively support computed fields, but with a bit of TypeScript magic, you can define a function that accepts a generic as an input then extend that generic to ensure it conforms to a specific structure. Finally, you can return that generic with additional computed fields. Let's see how that might look:
> Prisma Client extensions are currently in [Preview](/about/prisma/releases#preview).

The following example illustrates how to create a [Prisma Client extension](/concepts/components/prisma-client/client-extensions) that adds a `fullName` computed field at runtime to the `User` model in a Prisma schema.

<TabbedContent tabs={[<FileWithIcon text="Prisma Client extension" icon="code"/> ,<FileWithIcon text="Prisma schema" icon="file"/>]}>

<tab>

<CodeWithResult>

<cmd>

```ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient().$extends({
result: {
user: {
fullName: {
needs: { firstName: true, lastName: true },
compute(user) {
return `${user.firstName} ${user.lastName}`
},
},
},
},
})

async function main() {
/**
* Example query containing the `fullName` computed field in the response
*/
const user = await prisma.user.findFirst()
}

main()
```

</cmd>
<cmdResult>

```js no-copy
{
id: 'clgzkgy2j00004tc6iwu3gkzu',
firstName: 'Aurelia',
lastName: 'Schneider',
email: 'Jalen_Berge40@hotmail.com',
fullName: 'Aurelia Schneider',
}
```

</cmdResult>

</CodeWithResult>

</tab>

<tab>

```prisma copy
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

generator client {
provider = "prisma-client-js"
previewFeatures = ["clientExtensions"]
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?

posts Post[]
}

model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(true)
content String?

authorId Int?
author User? @relation(fields: [authorId], references: [id])
}
```

</tab>

</TabbedContent>

The computed fields are type-safe and can return anything from a concatenated value to complex objects or functions that can act as an instance method for your models.

## Using a computation function

Prisma Client does not yet natively support computed fields, but, you can define a function that accepts a generic type as an input then extend that generic to ensure it conforms to a specific structure. Finally, you can return that generic with additional computed fields. Let's see how that might look:

<TabbedContent tabs={[<FileWithIcon text="TypeScript" icon="code"/>, <FileWithIcon text="JavaScript" icon="code"/>]}>

Expand Down Expand Up @@ -74,7 +171,8 @@ A `WithFullName<User>` return type has also been defined, which takes whatever `

With this function, any object that contains `firstName` and `lastName` keys can compute a `fullName`. Pretty neat, right?

## Going Further
## Going further

- Learn how you can use [Prisma Client extensions](/concepts/components/prisma-client/client-extensions) to add a computed field to your schema — [example](https://github.com/prisma/prisma-client-extensions/tree/main/computed-fields).
- Learn how you can move the `computeFullName` function into [a custom model](/concepts/components/prisma-client/custom-models).
- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/3394) to add native support to Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case!
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ In the TypeScript example, we've provided two generics: `User` and `Key`. The `K

These generics flow through the logic, returning a `User` that omits the list of `Key`s provided.

## Going Further
## Going further

- Learn how you can move the `exclude` function into [a custom model](/concepts/components/prisma-client/custom-models).
- There's an [outstanding feature request](https://github.com/prisma/prisma/issues/5042) to add exclude support natively in Prisma Client. If you'd like to see that happen, make sure to upvote that issue and share your use case!
Loading