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.