Skip to Content
ControllerVovkRequest type

VovkRequest type

Request object accepted by controller methods is represented by VovkRequest type. It extends NextRequest type from Next.js by defining input body and query (but also params, see below) types. You can use it to strictly type built-in response methods like req.json() and req.nextUrl.searchParams that originally purely typed as any and string | null.

import { put, type VovkRequest } from 'vovk'; export default class UserController { @put('{id}') static async updateUser(req: VovkRequest<{ foo: 'foo' }, { bar: 'bar' }>, { id }: { id: string }) { const data = await req.json(); // data type is { foo: 'foo' } const bar = req.nextUrl.searchParams.get('bar'); // bar type is 'bar' // ... } }

Validation libraries define type of request by themselves so you don’t usually need to use VovkRequest manually.

Also notice the second argument of the handler method. It is used to define the type of parameters extracted from the request URL.

req.vovk

To avoid tweaking the original NextRequest object, Vovk introduces req.vovk object that contains additional utilities for request processing. Some validation libraries (like vovk-dto) can override the functions to return a different type, such as a DTO class instance.

async req.vovk.body()

Function req.vovk.body returns the request body serialized as an object. In most cases, it behaves the same as req.json() method.

import { post, type VovkRequest } from 'vovk'; export default class UserController { @post() static async createUser(req: VovkRequest<{ foo: string }>) { const body = await req.vovk.body(); // body type is { foo: string } // ... } }

req.vovk.query()

Function req.vovk.query returns serialized query parameters of the request.

import { get, type VovkRequest } from 'vovk'; export default class UserController { @get() static async getUser(req: VovkRequest<null, { id: string }>) { const query = req.vovk.query(); // query type is { id: string } // ... } }

Nested queries

The nested data is serialized as a query string with square brackets. It’s commonly known as β€œPHP-style query string notation” or β€œbracket notation”.

In this notation:

  • Square brackets [ ] are used to denote keys for arrays or nested objects.
  • Sequential numeric indices (e.g., [0], [1]) represent array elements.
  • Named keys (e.g., [f], [u]) represent properties of an object.
  • The structure can be nested to arbitrary depth, allowing for complex hierarchical data.

The following query string:

?simple=value&array[0]=first&array[1]=second&object[key]=value&nested[obj][prop]=data&nested[arr][0]=item1&nested[arr][1]=item2&complex[items][0][name]=product&complex[items][0][price]=9.99&complex[items][0][tags][0]=new&complex[items][0][tags][1]=featured

Is parsed as:

{ simple: "value", array: ["first", "second"], object: { key: "value" }, nested: { obj: { prop: "data" }, arr: ["item1", "item2"] }, complex: { items: [ { name: "product", price: "9.99", tags: ["new", "featured"] } ] } }

req.vovk.params()

Function req.vovk.params returns serialized parameters of the request. In order to define proper typing for it you need to use 3rd generic argument for VovkRequest type.

import { get, type VovkRequest } from 'vovk'; export default class UserController { @get('{id}') static async getUser(req: VovkRequest<null, null, { id: string }>) { const params = req.vovk.params(); // params type is { id: string } // ... } }

async req.vovk.form()

Function req.vovk.form returns FormData of the request serialized as an object. It’s a type-safe alternative to req.formData() that reads form data and uses Object.fromEntries(formData.entries()) to convert it to a normal object. The generic argument defines the shape of the form data.

interface Data { foo: string; bar: string; file: File; } const formDataObject = await req.vovk.form<Data>(); // the same as // const body = await req.formData(); // const formDataObject = Object.fromEntries(body.entries()) as Data;

When validation libraries is used with isForm option, the req.vovk.form method will be typed automatically.

req.vovk.meta()

Function req.vovk.meta allows to get and set meta information of the request. The only argument is optional and, if provided, it sets meta information extending the existing meta information and returns the current. If empty, it returns the current meta information. In order to define the type of meta information, you need to pass a generic type.

interface RequestMeta { foo: string; bar: string; currentUser: User; } // set foo and bar meta information req.vovk.meta<RequestMeta>({ foo: 'foo', bar: 'bar' }); // set currentUser meta information req.vovk.meta<RequestMeta>({ currentUser: user }); // get meta information const meta = req.vovk.meta<RequestMeta>(); // meta type is RequestMeta including foo, bar and currentUser

The metadata is usually set in a custom decorator.

Metadata set by client

Besides normal inputs such as body, query, and params, RPC module methods also accept meta object as an input that is sent by setting x-meta header with JSON-serialized data of this object. On the server side this header is parsed and set as xMetaHeader property of the metadata object.

On client-side, pass meta object to the RPC method:

import { UserRPC } from 'vovk-client'; const user = await UserRPC.getUsers({ meta: { hello: 'world' }, });

On server-side, you can access it like this:

import { get, type VovkRequest } from 'vovk'; export default class UserController { @get() static async getUsers(req: VovkRequest) { const { xMetaHeader } = req.vovk.meta<{ xMetaHeader: { hello: string } }>(); console.log(xMetaHeader); // Outputs: { hello: 'world' } // ... } }
Last updated on