Skip to Content
ControllerFormData

FormData

To accept form data requests, set the isForm option to true in the validation library wrapper.

import { withZod } from 'vovk-zod'; import { z } from 'zod'; import { post, prefix } from 'vovk'; export default class UserController { @post() static createUser = withZod({ isForm: true, body: z.object({ email: z.string().email(), name: z.string().min(2).max(100), }), async handle(req) { // ... }, }); }

This makes req typed as VovkRequest<FormData, ...>, and the RPC method’s body type is inferred as FormData.

import { UserRPC } from 'vovk-client'; const formData = new FormData(); formData.append('email', 'user@example.com'); formData.append('name', 'John Doe'); await UserRPC.createUser({ body: formData, });

Accessing Form Data

Access form data using the built‑in req.formData() method.

//... export default class UserController { @post() static createUser = withZod({ isForm: true, body: z.object({ /* ... */}), async handle(req) { const formData = await req.formData(); // FormData instance // ... }, }); }

You can also use req.vovk.form(), which serializes the form data into a plain object.

// ... export default class UserController { @post() static createUser = withZod({ isForm: true, body: z.object({ email: z.string().email(), name: z.string().min(2).max(100), }), async handle(req) { const form = await req.vovk.form(); // { email: 'user@example.com', name: 'John Doe' } // ... }, }); }

Using Lists

If the form data can contain one or more values for the same key, use a union schema of the value type and an array of the value type, because FormData doesn’t distinguish between single and multiple values.

import { withZod } from 'vovk-zod'; import { z } from 'zod'; import { post, prefix } from 'vovk'; export default class UserController { @post() static createUser = withZod({ isForm: true, body: z.object({ tags: z.union([z.array(z.string()), z.string()]), }), async handle(req) { const form = await req.vovk.form(); // { tags: ['tag1', 'tag2'] } or { tags: 'tag1' } }, }); }

One value:

import { UserRPC } from 'vovk-client'; const formData = new FormData(); formData.append('tags', 'tag1'); await UserRPC.createUser({ body: formData, });

Multiple values:

import { UserRPC } from 'vovk-client'; const formData = new FormData(); formData.append('tags', 'tag1'); formData.append('tags', 'tag2'); await UserRPC.createUser({ body: formData, });

The same recommendation applies to files:

import { withZod } from 'vovk-zod'; import { z } from 'zod'; import { post, prefix } from 'vovk'; export default class UserController { @post() static createUser = withZod({ isForm: true, body: z.object({ files: z.union([z.array(z.file()), z.file()]), }), async handle(req) { const form = await req.vovk.form(); // { files: [File, File] } or { files: File } // ... }, }); }

Client-Side Validation Limitations

Note that vovk-ajv does not currently support the OpenAPI‑compatible format: "binary", and file size, type, etc., are not validated on the client side.

Last updated on