Skip to content

finom/vovk-zod

Repository files navigation


vovk

plus

vovk-zod

Isomorphic Zod validation for Vovk.ts controllers on server and client

npm version TypeScript Build status

vovk-zod exports vovkZod decorator fabric that validates request body and incoming query string with Zod models.

// /src/models/user/UserController.ts
import { z } from 'zod';
import vovkZod from 'vovk-zod';
import { put, type VovkRequest } from 'vovk';
import UserService from './UserService';

const UpdateUserModel = z.object({
    name: z.string(),
    email: z.string(),
}).strict();

const UpdateUserQueryModel = z.object({
    id: z.string(),
}).strict();

export default class UserController {
    @put()
    @vovkZod(UpdateUserModel, UpdateUserQueryModel)
    static updateUser(
        req: VovkRequest<z.infer<typeof UpdateUserModel>, z.infer<typeof UpdateUserQueryModel>>
    ) {
        const { name, email } = await req.json();
        const id = req.nextUrl.searchParams.get('id');

        return UserService.updateUser(id, { name, email });
    }
}
'use client';
import React from 'react';
import { UserController } from 'vovk-client';

const MyPage = () => {
    useEffect(() => {
        void UserController.updateUser({
            query: { id: '696969' },
            body: { name: 'John Doe', email: '[email protected]' },
            // optionally, disable client validation for debugging purpose
            disableClientValidation: true, 
        }).then(/* ... */);
    }, []);

    return (
        // ...
    )
}

export default MyPage;

When vovk-zod is installed zodValidateOnClient is enabled by default as validateOnClient config option to validate incoming reqests on the client-side. Please check decorators docs for more info.

Working with FormData

The library doesn't support FormData validation, but you can still validate query by setting body validation to null. At the same time VovkRequest should get FormData type as the first generic parameter in order to make types of vovk-client infer body as expected.

// ...

export default class HelloController {
    @post()
    @vovkZod(null, z.object({ something: z.string() }).strict())
    static postFormData(req: VovkRequest<FormData, { something: string }>) {
        const formData = await req.formData();
        const something = req.nextUrl.searchParams.get('something');

        // ...
    }
}

How it works

The library (as well as Vovk.ts itself) is built thanks to fantastic job made by other people.

  • When @vovkZod is initialised, it converts Zod schemas to JSON Schemas with zod-to-json-schema and makes metadata handler to receive it as client validation object.
  • @vovkZod performs Zod validation on server-side.
  • When clientized controller method gets called zodValidateOnClient performs validation on client-side with Ajv. Since client-side and server-side validation is implemented by different libraries, error messages are going to be different.