Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
January 8, 2023 07:27 pm GMT

Zod: The Next Biggest thing after Typescript

Content

  • Content
  • Introduction
  • Understanding the why
  • Managing contracts
  • Traditional approach
  • The Zod Way
  • Simple example
    1. Defining the Zod schema
    2. Convert Zod schema into Typescript types
    3. Create some pizzas
    4. Run-time validations
  • Establishing a new standard - End-to-end Typesafety
  • Striking the right balance
  • Conclusion

Introduction

I recently came across a neat little library called Zod.

My first reaction looking through the documentation was this looks interesting.

It wasnt until I tried it that I felt the difference The difference is MASSIVE.

Nothing comes close to it.

Its a different approach but once you try out Zod, I think you would know what I mean.

In my opinion, Zods approach hits the right balance between robust code, and developer experience (DX) when working with data validation in Typescript.

Disclaimer: After reading this article, you may not want to use any other validation library! (Youve been warned)

Understanding the why

Typescript introduces great checks during development by statically checking the code meets the contract defined in the types.

This works for most cases, however, in production, it becomes more complex.

Image Illustration of compiling Typescript to Javascript

  • Javascript - Typescript does not run in production, it compiles down to Javascript

  • Loose contracts - Typescript type contracts are not enforced when code is compiled to Javascript

  • Data predictability - Data quality and sources tend to be un-predictable in complex system running in production

Hence, for this reasons, there is a need for run-time validation to enforces these contracts within Javascript.

Managing contracts

When we work with functions in Javascript, it consists of inputs and outputs.

A certain set of inputs will give you certain set of outputs This is what I call a contract.

It is not too different from a contract you sign a contract with your bank or insurance or telecommunication company.

There is a certain level of guarantee when you sign on for their services.

Image Illustration inputs and outputs in a Javascript function

By establishing a contract it forces us to narrow the scope of the inputs and outputs.

In essence, you reduce the surface area hence making the function more predictable.

Now comes the question, how is this done in Javascript ?

Traditional approach

The traditional approach to achieve this is installing some sort of validation library (ie Joi, Ajv etc).

The most common application for this is managing form inputs with user input data.

However, it doesnt have to be only for forms, you can use run-time validation for anything.

It will make your code more robust because any sort of data not meeting a contract will be considered a failure.

There is not in between or edge cases. It makes the code very strict.

Image Illustration inputs and outputs in a Javascript function

The trade off with these libraries is there is a lot of duplication - like A LOT in a large code base.

Not only do you have to define the Typescript types, you also have to define validation schemas.

Talk about doubling the work...

If you ever needed to do this, you know this pain. Also, lets not even get into how much this bloats up the code base .

Then next thing you know, you start doing this

Image Meme of using ts-ignore and any in Typescript

Well, is there a better way ?

What if I told you there is...

You can probably guess it. Ill give you a hint, it start with a Z.

The Zod Way

Enter Zod.

Here is where Zod differs from all the other validation libraries.

How is it different from everything else ? Zod takes a schema first approach.

Meaning, you start with your validation schema (Zod schema).

Then, this Zod schema becomes your validations, and your types.

So, you get the best of both worlds!

Not only do you you get run-time validations from the schema but you also get the types by converting the schema into Typescript.

Image Illustration of Zods schema first approach

Neat huh ? Talk about super charging productivity and developer experience

Simple example

Enough of the illustrations, I want to see some code!

Lets go through a simple example.

Lets say were a pizza shop, and we need to design some schemas for our website.

1. Defining the Zod schema

import { z } from 'zod';// Zod schemaconst pizzaSchema = z.object({  sauce: z.string(),  ingredients: z.array(z.string()),});

2. Convert Zod schema into Typescript types

import { z } from 'zod';// Zod schemaconst pizzaSchema = z.object({  sauce: z.string(),  ingredients: z.array(z.string()),});// TypeScript typeexport type IPizza = z.infer<typeof pizzaSchema>;

3. Create some pizzas

import { z } from 'zod';// Zod schemaconst pizzaSchema = z.object({  sauce: z.string(),  ingredients: z.array(z.string()),});// TypeScript typeexport type IPizza = z.infer<typeof pizzaSchema>;const pepperoniPizza: IPizza = {  sauce: 'tomato',  ingredients: [    'cheese',    'pepperoni',  ],};console.log(pepperoniPizza);// => { sauce: 'tomato', ingredients: [ 'cheese', 'pepperoni' ] }const hawaiianPizza: IPizza = {  sauce: 'tomato',  ingredients: [    'cheese',    'pineapple',    'ham',  ],};console.log(hawaiianPizza);// => { sauce: 'tomato', ingredients: [ 'cheese', 'pineapple', 'ham' ] }

4. Run-time validations

import { z } from 'zod';// Zod schemaconst pizzaSchema = z.object({  sauce: z.string(),  ingredients: z.array(z.string()),});// TypeScript typeexport type IPizza = z.infer<typeof pizzaSchema>;const pepperoniPizza: IPizza = {  sauce: 'tomato',  ingredients: [    'cheese',    'pepperoni',  ],};console.log(pizzaSchema.parse(pepperoniPizza));// => { sauce: 'tomato', ingredients: [ 'cheese', 'pepperoni' ] }const hawaiianPizza: IPizza = {  sauce: 'tomato',  ingredients: [    'cheese',    'pineapple',    'ham',  ],};console.log(pizzaSchema.parse(hawaiianPizza));// => { sauce: 'tomato', ingredients: [ 'cheese', 'pineapple', 'ham' ] }console.log(pizzaSchema.parse(null)); // throws ZodError

Striking the right balance

Most libraries will force you to the right thing by sacrificing developer experience (DX).

Thats not the case with Zod, the team really got it just right.

When using Zod, you can do the right thing without any friction at all.

It works seamlessly with Typescript.

Now thats a tool worth looking into.

Establishing a new standard - End-to-end Typesafety

Zod opens the door up to interesting tools like tRPC which takes the developer experience (DX) to the next level.

The big idea with tRPC is you can define a backend endpoint with a schema, then have automatically have autocompletion on the client side.

This raises the standard for all other frameworks to provide integrations with tRPC or create a similar experience.

I see tRPC, and tRPC like experiences being more prevalent in the future merely for the speed of development and developer experience (DX) it provides.

Conclusion

So, there you have it. Thats Zod.

A library that gives you this seamless experience for designing robust code using both run-time (schema) and static (types) validations.

Before we go, lets do a recap.

Image Zod takeaways

And... thats all for now, stay tuned for more!

If you found this helpful or learned something new, please do share this article with a friend or co-worker (Thanks!)

Also published at - jerrychang.ca

Note:Yup also supports the ability to infer types from the data schema defined.You can do something like yup.InferType in order to get your Typescript type.Just throwing it out there as another great library that allows you to do similar things as Zod.

Original Link: https://dev.to/jareechang/zod-the-next-biggest-thing-after-typescript-4phh

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To