- Published on
Fixing Zod 'Expected String, Received Undefined' Error in TypeScript
- Authors
- Name
- Ripal & Zalak
Fixing Zod 'Expected String, Received Undefined' Error in TypeScript
When working with Zod for schema validation in a TypeScript application, you might encounter the error:
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["name"],
"message": "Required"
}
This error occurs when Zod expects a string but receives undefined
. In this post, we'll explore why this happens and how to resolve it effectively.
Problem
The error arises when the request body (req.body
) doesn't contain the required fields, or they are improperly formatted. This is often due to:
- A missing or misconfigured middleware to parse request bodies (e.g.,
express.json()
for JSON payloads). - Invalid or incomplete data sent in the request.
Example Scenario
Here’s a typical example where this error might occur:
Prisma Model
model Client {
id String @id @default(uuid())
email String @unique
name String
password String
created_at DateTime @default(now())
updated_at DateTime @updatedAt
reviews ProductReview[] @relation("client")
adm Boolean @default(false)
@@map("clients")
}
Express Route and Controller
Route:
const authController = new AuthController()
router.route('/register').post(authController.registerClient)
Controller:
import { z } from 'zod'
import { Request, Response } from 'express'
export class AuthController {
async registerClient(req: Request, res: Response) {
const createClientBody = z.object({
name: z.string(),
email: z.string(),
password: z.string(),
})
const { name, email, password } = createClientBody.parse(req.body)
const response = await prisma.client.create({
data: { name, email, password },
})
return res.status(201).send({ response })
}
}
Error Example
Sending a request with an empty body ({}
) results in:
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["name"],
"message": "Required"
}
Solution
Step 1: Add Middleware for Body Parsing
Ensure that your Express app includes the correct middleware to parse JSON request bodies:
import express from 'express'
const app = express()
app.use(express.json())
Without this middleware, req.body
will be empty, causing Zod validation to fail.
Step 2: Validate and Handle Missing Fields Gracefully
To make the schema more robust, allow fields to be optional or handle missing values:
const createClientBody = z.object({
name: z.string().optional().default(''),
email: z.string().optional().default(''),
password: z.string().optional().default(''),
})
Alternatively, use .nullish()
for nullable or undefined values:
const createClientBody = z.object({
name: z.string().nullish(),
email: z.string().nullish(),
password: z.string().nullish(),
})
Step 3: Debug Incoming Data
Log req.body
before parsing to confirm the payload is being sent correctly:
console.log(req.body)
Step 4: Use Proper Error Handling
Wrap the validation in a try-catch block to provide meaningful error responses:
try {
const { name, email, password } = createClientBody.parse(req.body)
const response = await prisma.client.create({
data: { name, email, password },
})
res.status(201).send({ response })
} catch (e) {
if (e instanceof z.ZodError) {
res.status(400).send({ errors: e.errors })
} else {
res.status(500).send({ error: 'Internal server error' })
}
}
FAQ
req.body
empty?
1. Why is This happens when the express.json()
middleware is not added to your Express app. Add it as shown in Step 1.
.nullish()
do in Zod?
2. What does The .nullish()
method allows a field to accept null
or undefined
values in addition to the expected type.
3. How can I ensure all required fields are present?
Use the default z.string()
schema and provide clear error messages when fields are missing:
z.string({ required_error: 'This field is required' })
4. Can I provide default values?
Yes! Use .default(value)
to provide default values for optional fields:
z.string().optional().default('Default Value')