Basic usage
This page will walk you through the basics of creating schemas, parsing data, and using inferred types. For complete documentation on Zod's schema API, refer to Defining schemas.
Defining a schema
Before you can do anything else, you need to define a schema. For the purposes of this guide, we'll use a simple object schema.
Parsing data
Given any Zod schema, use .parse to validate an input. If it's valid, Zod returns a strongly-typed deep clone of the input.
Note — If your schema uses certain asynchronous APIs like async refinements or transforms, you'll need to use the .parseAsync() method instead.
Handling errors
When validation fails, the .parse() method will throw a ZodError instance with granular information about the validation issues.
To avoid a try/catch block, you can use the .safeParse() method to get back a plain result object containing either the successfully parsed data or a ZodError. The result type is a discriminated union, so you can handle both cases conveniently.
Note — If your schema uses certain asynchronous APIs like async refinements or transforms, you'll need to use the .safeParseAsync() method instead.
Inferring types
Zod infers a static type from your schema definitions. You can extract this type with the z.infer<> utility and use it however you like.
In some cases, the input & output types of a schema can diverge. For instance, the .transform() API can convert the input from one type to another. In these cases, you can extract the input and output types independently:
Matching an existing type
Sometimes the type comes first: a database model, a type from a generated client, an interface you don't own. Pass it to z.toZod<T>() and TypeScript checks that the schema's output type is exactly T.
The check is exact type equality, so any drift from the type is a compile error:
The usual alternative is satisfies z.ZodType<Player>, which only checks assignability. It catches a missing required key, but extra keys, omitted optional keys, and a bare z.any() all slip through:
Exactness applies to the type as TypeScript wrote it, so an intersection target matches .and() rather than .safeExtend():
Flatten the target and .safeExtend() matches instead:
Now that we have the basics covered, let's jump into the Schema API.

