Added AI skills
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
---
|
||||
title: Use parseAsync for Async Refinements
|
||||
impact: CRITICAL
|
||||
impactDescription: Using parse() with async refinements throws an error; async validation silently fails or crashes the application
|
||||
tags: parse, async, parseAsync, refinement
|
||||
---
|
||||
|
||||
## Use parseAsync for Async Refinements
|
||||
|
||||
If your schema uses `refine()` or `superRefine()` with async validation (like database lookups), you must use `parseAsync()` or `safeParseAsync()`. Using synchronous `parse()` with async refinements throws an error.
|
||||
|
||||
**Incorrect (sync parse with async refinement):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
const userSchema = z.object({
|
||||
email: z.string().email(),
|
||||
username: z.string().min(3),
|
||||
}).refine(
|
||||
async (data) => {
|
||||
// Async database check
|
||||
const exists = await db.users.findByEmail(data.email)
|
||||
return !exists
|
||||
},
|
||||
{ message: 'Email already registered' }
|
||||
)
|
||||
|
||||
// This throws an error!
|
||||
const user = userSchema.parse(formData)
|
||||
// Error: Async refinement encountered during synchronous parse operation.
|
||||
// Use .parseAsync instead.
|
||||
```
|
||||
|
||||
**Correct (using parseAsync):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
const userSchema = z.object({
|
||||
email: z.string().email(),
|
||||
username: z.string().min(3),
|
||||
}).refine(
|
||||
async (data) => {
|
||||
const exists = await db.users.findByEmail(data.email)
|
||||
return !exists
|
||||
},
|
||||
{ message: 'Email already registered' }
|
||||
)
|
||||
|
||||
// Use parseAsync for async refinements
|
||||
const user = await userSchema.parseAsync(formData)
|
||||
|
||||
// Or safeParseAsync for error handling
|
||||
const result = await userSchema.safeParseAsync(formData)
|
||||
if (!result.success) {
|
||||
console.log(result.error.issues)
|
||||
}
|
||||
```
|
||||
|
||||
**Async transforms also require parseAsync:**
|
||||
|
||||
```typescript
|
||||
const enrichedUserSchema = z.object({
|
||||
userId: z.string().uuid(),
|
||||
}).transform(async (data) => {
|
||||
// Async data enrichment
|
||||
const user = await db.users.findById(data.userId)
|
||||
return {
|
||||
...data,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
}
|
||||
})
|
||||
|
||||
// Must use parseAsync
|
||||
const enrichedUser = await enrichedUserSchema.parseAsync({ userId: '123' })
|
||||
```
|
||||
|
||||
**Pattern for API routes:**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
const registerSchema = z.object({
|
||||
email: z.string().email(),
|
||||
password: z.string().min(8),
|
||||
}).superRefine(async (data, ctx) => {
|
||||
const existingUser = await db.users.findByEmail(data.email)
|
||||
if (existingUser) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
path: ['email'],
|
||||
message: 'Email already registered',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const body = await req.json()
|
||||
|
||||
// Always use safeParseAsync with async schemas
|
||||
const result = await registerSchema.safeParseAsync(body)
|
||||
|
||||
if (!result.success) {
|
||||
return NextResponse.json({ errors: result.error.issues }, { status: 400 })
|
||||
}
|
||||
|
||||
// Proceed with registration
|
||||
}
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- Schemas with only synchronous validation (use parse/safeParse)
|
||||
- When async validation can be moved outside Zod (validate, then check)
|
||||
|
||||
Reference: [Zod API - parseAsync](https://zod.dev/api#parseasync)
|
||||
Reference in New Issue
Block a user