Zod simple tutorial
Tags: typescript, node, beginners, tutorial
Published: 2022-04-22 14:54:00

## Node-Typescript-Zod
In the following example will be creating a simple REST API, and validating it with *zod*.

[You can contact me by telegram if you need to hire a Full Stack developer.](https://t.me/rtagliajs).

You can also contact me by **discord Appu#9136**.

## creating our project

- open your terminal and type the following
- mkdir node-typescript-zod-tut
- cd node-typescript-zod-tut
- npm init --y
- code .

## Packages
- express
- zod
- typescript
- ts-node-dev
- @types/express

### Production packages
```console
npm i express zod
```

### Development packages
```console
npm i typescript ts-node-dev @types/express -D
```

## project file structure:

node-typescript-zod-tutorial/
├── node_modules/
├── src/
│   ├── routes/
│   ├── schema/
│   └── index.ts
├── tsconfig.json
└── package.json

## Project setup

1- Add this line in your `package.json`, with ts-node-dev we can run .ts files, --respawn to re-run automatically after a change in our file.


```json
 "scripts": {
    "dev": "ts-node-dev --respawn src/index.ts"
  },
```

2- Type the following line in your console,<code>npx tsc --init </code> it will create a tsconfig.json, now in our tsconfig.json file let's use "ctrl+f" to find rootDir and outDir, uncomment rootDir and type this <code>"rootDir": "./src", </code> uncomment outDir and type this <code>"outDir": "./dist", </code>.


### Let's code

1-creating our index.ts
`index.ts`
```js
import express from 'express'


//initialiaztion
const app = express()


//server
app.listen(3000, () => {
  console.log('listening on port 3000')
})
```


Now let's run <code>npm run dev</code> and we should get this:
```console
listening on port 3000
```

2- Let's go to our routes folder and create this file <code>contacts.route.ts</code>

`contacts.route.ts`
```js
import { Router, Require, Response } from "express";

const route = Router()

route.post('/contacts', (req: Require, res: Response) => {
  console.log(req.body)
  res.json({message: 'contact created'})
})

export default route
```


Let's go back to **index.ts** and import our route.


`index.ts`
```js
import express from 'express'
import contactsRoutes from './routes/contacts.routes'

//initialiaztion
const app = express()

//middlewares
app.use(express.json())

//routes
app.use(contactsRoutes)

//server
app.listen(3000, () => {
  console.log('listening on port 3000')
})
```

let's try our API, I am gonna use the [REST client VScode extension](https://marketplace.visualstudio.com/items?itemName=humao.rest-client), but feel free to use [postman](https://www.postman.com/), [insomnia](https://insomnia.rest/), or whatever you want.

We want to pass some fields, let's use contact first name, last name, email and a phone number.


![route-test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3p9uz388v0gewuj8jic.png)

3- Let's create our *zod* schema, go to our schema folder and create **contacts.schema.ts** file.

In this code we are creating our ContactSchema where firstName field should be a string with `.string()` and cannot be empty using `.nonempty()`.

`contacts.schema.ts`
```js
import { z } from "zod";

export const ContactSchema = z.object({
  firstName: z
  .string()
  .nonempty(),
})
```

Let's go back to `contacts.route.ts` and import our schema.

```js
import { Router, Request, Response } from "express";
import { ContactSchema } from "../schema/contacts.schema";

const route = Router()

route.post('/contacts',(req: Request, res: Response) => {
  console.log(req.body)
  try {
    const result = ContactSchema.parse(req.body);
    console.log(result)
    res.json({messasge: 'contact created'})
  } catch (error) {
    return res.status(500).json({ message: error });
  }
})

export default route
```
Let's try out our post route again and see what happens.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ua32454x4o99rdeyf6p0.png)

Now we are getting some errors, `"expected": "string",` because `"message": "Expected string, received number"`.

Let's fix a little bit our code to display our error message in a better way, first let's import `ZodError` from *zod*, and change the catch block to display only the error messages from *zod* and if the error is not coming from *zod* display the server error 

```js
import { Router, Request, Response } from "express";
import { ContactSchema } from "../schema/contacts.schema";
import { ZodError }from "zod";

const route = Router()

route.post("/contacts", (req: Request, res: Response) => {
  console.log(req.body);
  try {
    const result = ContactSchema.parse(req.body);
    console.log(result);
    res.json({ messasge: "contact created" });
  } catch (error) {
    if (error instanceof ZodError) {
      return res
        .status(400)
        .json(error.issues.map((issue) => ({ message: issue.message })));
    }
    return res.status(500).json({ message: error });
  }
});

export default route
```

Now we're getting this message .

![apitest4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dp0w7ms3ph19u9071twh.png)

Now let's the with `firstName` field empty.

![apitest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceatwyun5aanvocn5muc.png)

We can also change the error message, let's go back to `contacts.schema.ts`, and add this to `.nonempty()`

```js
import { z } from "zod";

export const ContactSchema = z.object({
  firstName: z
  .string()
  .nonempty('first name is required'),
})
```
Now we're getting this.

![apitest5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkgrm00a45ms8na3pfzc.png)

Let's add the rest of the fields.

```js
import { z } from "zod";

export const ContactSchema = z.object({
  firstName: z
  .string()
  .nonempty('Name is Required'),
  lastName: z
  .string()
  .nonempty('Last Name is Required'),
  email: z
  .string()
  .email({message: 'Email is not valid'})
  .nonempty('Email is Required'),
  phone: z
  .string()
  .nonempty('Phone is Required'),
})
```

Let's do a couple of tests.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvagcl6lapyob8rpu8ig.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dnmeio4xru7pbfwempl0.png)

That would be a basic idea of validation with Zod.

[You can contact me by telegram if you need to hire a Full Stack developer.](https://t.me/rtagliajs).

[You can also contact me by discord.](Appu#9136).

I hope you find this helpful. please let me know what you think, thank you.