`schema.parse()` is equivalent to pydantic `MyModel.parse_obj()` and throws if invalid.
`const result = schema.safeParse(val)` does not throw, and if `!result.success`, `result.error` contains a `ZodError` object with all the details. It's common to further transform this error object, perhaps to make it more human-readable or suitable for an API response.
Typically I find myself using `safeParse` far more than `parse`.