API Reference
Core
initSegment
Creates the Next.js route handlers of an Optional Catch-all Segment route.
It takes:
segmentName?: string– the segment name, as used in the route. Defaults to an empty string (the root segment).controllers: Record<string, Function>– the controllers, keyed by RPC module name.exposeValidation?: boolean–falseleaves the validation schemas out of the emitted schema, so the client code doesn’t see them. The declared content types stay, since the clients encode the body by them. Defaults totrue.emitSchema?: boolean–falseemits no schema for the segment. Defaults totrue.onError?: (err: Error, req: VovkRequest) => void | Promise<void>– called when a controller throws, for example to log the error. The second argument is the request, with its URL, authorization data and other details.onBefore?: (req: VovkRequest) => void | Promise<void>– called with the request after the route’sbeforeand before the custom decorators and the handler run. An error it throws becomes the response.onSuccess?: (result: unknown, req: VovkRequest) => void | Promise<void>– called with the handler’s result and the request once the handler returns. For a stream, that is when the stream starts, not when it ends.
JSONLinesResponder
JSONLinesResponder creates a response in the JSON Lines format. Its methods send JSON objects, one per line, to the response stream.
const responder = new JSONLinesResponder<IterationType>(req, ({ readableStream, headers }) => new Response(readableStream, { headers }));
await responder.send({ message: 'Hello' });
await responder.send({ message: 'World' });It takes:
req?: Request– the incoming request.getResponse?: (responder: JSONLinesResponder<T>) => Response– an optional function that creates a customResponse.
Methods and properties:
send(item: T): Promise<void>– sends one JSON object as a line of the stream.close(): Promise<void>– closes the stream: nothing more is sent.throw(err: Error): Promise<void>– sends an error to the client and closes the stream.response: Response– theResponsethat the Next.js route handler returns.headers: Record<string, string>– thecontent-typeheader of the response.readableStream: ReadableStream<Uint8Array>– the stream used as the response body.
See the JSON Lines page.
toDownloadResponse
Creates a Response with file or binary content and the matching headers, such as Content-Type and Content-Disposition. Return it from a controller. MCP tool output formatting accepts such a Response too.
Arguments:
content: Blob | File | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array> | string— the file content.opts?:filename?: string— setsContent-Disposition: attachment; filename=....type?: string— overrides theContent-Typeof the response, such asaudio/mpeg,text/csvorapplication/pdf.headers?: Record<string, string>— more headers for the response.
Serving an audio file:
import { get, toDownloadResponse } from 'vovk';
export default class MediaController {
@get('audio')
static getAudio() {
return toDownloadResponse(buffer, {
filename: 'track.mp3',
type: 'audio/mpeg',
headers: { 'x-hello': 'world' },
});
}
}Decorators
@get, @post, @put, @patch, @del, @head, @options
These decorators set the HTTP method of a handler. A HEAD request to a path without a @head route gets the response of the GET route: its status and headers, without the body. A request to a path that has routes only for other methods gets 405 and an Allow header that lists those methods. The decorators take two optional arguments:
path? = ''– the path of the route.opts?: { cors?: boolean, headers?: Record<string, string>, staticParams?: Record<string, string>[], before?: (req: VovkRequest) => unknown }– route options:cors: trueadds CORS headers to the response and answers theOPTIONSpreflight. The preflight allows and lists only the methods whose route on that path hascors, so a browser can’t call a route without it from another origin. The preflight skips the decorator’sbeforeand the segment’sonBeforehooks.headersare added to the response.staticParams(@getonly) is an array of path parameter values, for example[{ id: '123' }].beforeruns on an HTTP request before the segment’sonBefore, the custom decorators and the handler, with the controller asthis. An error it throws becomes the response.
import { get, type VovkRequest } from 'vovk';
export default class HelloController {
@get('world/{id}', { cors: true, headers: { 'x-hello': 'world' }, staticParams: [{ id: '123' }] })
static getHelloWorld(req: VovkRequest, { id }: { id: string }) {
return { hello: 'world', id };
}
}Each HTTP method decorator has an .auto() form. It builds the path from the method name in kebab case and adds a {name} segment for each property of the procedure’s params schema, so getHelloWorld below is served at get-hello-world/{id}. It takes the same options as the decorator.
import { get, procedure } from 'vovk';
import { z } from 'zod';
export default class HelloController {
@get.auto({ cors: true, headers: { 'x-hello': 'world' }, staticParams: [{ id: '123' }] })
static getHelloWorld = procedure({
params: z.object({ id: z.string() }),
}).handle((req, { id }) => {
return { hello: 'world', id };
});
}@prefix
@prefix(p: string) adds a path in front of every endpoint of a controller. It’s optional.
import { prefix, get } from 'vovk';
@prefix('hello')
export default class HelloController {
@get('world')
static getHelloWorld() {
return { hello: 'world' };
}
}@operation
@operation(openAPIOperationObject) adds OpenAPI documentation to a procedure. It takes an object with summary, description and any other property of the OpenAPI Operation Object.
@operation.tool() adds AI tool data under the x-tool key of the operation object (see Deriving AI Tools), and @operation.error(status, message) documents an error response.
import { get, operation } from 'vovk';
export default class HelloController {
@operation.tool({
title: 'Get Hello World',
})
@operation({
summary: 'Get Hello World',
description: 'Returns a hello world message',
})
@get('world')
static getHelloWorld() {
return { hello: 'world' };
}
}@cloneControllerMetadata
A controller can be in several segments, and each segment runs its own hooks. If the composed client includes those segments, give the controller a different key in each: the client exports one module per name. To serve the same handlers under another prefix, or together with new handlers, extend the controller and apply @cloneControllerMetadata to the new class. It copies all metadata (routes, operations and so on) from the parent controller. The new class keeps the parent’s prefix unless it sets its own, as @prefix('v2') does below.
import { prefix, cloneControllerMetadata } from 'vovk';
import UserController from './user-controller';
@cloneControllerMetadata()
@prefix('v2')
export default class UserControllerV2 extends UserController {}Utils
createDecorator
createDecorator creates procedure decorators. Its first argument is a middleware function with these parameters:
req: VovkRequest– the request.next: () => Promise<any>– calls the next middleware or the handler.- Other arguments – the values passed to the decorator.
The optional second argument changes the procedure schema based on the decorator arguments.
import { createDecorator, get } from 'vovk';
const myDecorator = createDecorator(
(req, next, a: string, b: number) => {
// do something with the request
return next();
},
(a: string, b: number) => {
// change the schema here
}
);
export default class MyController {
@get.auto()
@myDecorator('foo', 1) // passes 'foo' as a, and 1 as b
static doSomething() {
// ...
}
}See the decorator docs.
fetcher
The default fetcher: the function the RPC client sends its requests with, unless you customize it. createFetcher creates a new one. Exported from vovk/fetcher.
createFetcher
Creates a custom fetcher for the RPC client. Its type parameter defines extra options for the RPC methods. It returns the fetcher function that the client is built with.
import { createFetcher } from 'vovk/fetcher';
export const fetcher = createFetcher<{
successMessage?: string; // "Successfully created a new user"
useAuth?: boolean; // if true, sets the Authorization header
someOtherCustomFlag?: boolean; // any other flag you pass to the RPC method
}>({
prepareRequestInit: async (init, { useAuth, someOtherCustomFlag }) => {
// ...
return {
...init,
headers: {
...init.headers,
...(useAuth ? { Authorization: 'Bearer token' } : {}),
},
};
},
transformResponse: async (data, { someOtherCustomFlag }) => {
// ...
return data;
},
onSuccess: async (data, { successMessage }) => {
if (successMessage) {
alert(successMessage);
}
},
onError: async (error) => {
alert(error.message);
},
});See the fetcher docs.
controllersToStaticParams
Lists the routes as static params, so the API is built at build time instead of on each request. Use it in the Next.js generateStaticParams() of [[...slug]]/route.ts.
Arguments:
controllers: Record<string, Function>— the controllers. It lists the path of every handler, whatever its HTTP method: the controller prefix and the handler path. A handler withstaticParamsgives one path per item, with the{name}placeholders filled in; other paths keep their placeholders.slugName = 'vovk'— (optional) the name of the Optional Catch‑all Segment param, the folder name in[[...<slugName>]]. Set it when the folder isn’t[[...vovk]].
Returns:
Array<Record<string, string[]>>— params objects forgenerateStaticParams(): one per path, such as{ vovk: ['hello', 'greeting.json'] }. The list starts with{ vovk: ['_schema_'] }for the schema endpoint whenNODE_ENVisdevelopment, or when there is no other path, asoutput: 'export'needs one.
// ...
export type Controllers = typeof controllers;
export function generateStaticParams() {
return controllersToStaticParams(controllers);
}
export const { GET } = initSegment({ controllers });The segment needs no dynamic export: Next.js pre-renders the listed paths with cacheComponents on or off.
For another slug folder, such as src/app/api/[[...custom]]/route.ts, pass its name as the second argument:
export function generateStaticParams() {
return controllersToStaticParams(controllers, 'custom');
}See the segment page.
multitenant
A Next.js proxy helper that routes subdomains to segments.
Returns:
action: 'rewrite' | 'redirect' | null— what the proxy should do.destination: string | null— the URL to rewrite or redirect to.message: string— what it decided and why, for debugging.subdomains: Record<string, string> | null— the wildcard subdomains from the request host.
// ... proxy ...
const { action, destination, message, subdomains } = multitenant({
requestUrl: request.url,
requestHost: request.headers.get('host') ?? '',
targetHost: process.env.VERCEL ? 'multitenant.vovk.dev' : 'localhost:3000',
overrides: {
// ...
},
});
console.log({ action, destination, message, subdomains });
// ...See the multitenant guide.
deriveTools
Turns RPC modules or controllers into AI tools that follow the standard-tool convention (the StandardToolV0 interface).
Options:
modules: Record<string, object>– module names mapped to objects with methods. Each method needs theschemaproperty that RPC methods and procedures have, and eitherisRPC: true(RPC modules) or the fn function (controllers).onExecute?: (result: unknown, tool: StandardToolV0, req: Pick<VovkRequest, 'vovk'> | null) => void– called after each successful tool call. For a controller,reqis the request of the call, withreq.vovk; for an RPC method, it’snull.onError?: (e: Error, tool: StandardToolV0, req: Pick<VovkRequest, 'vovk'> | null) => void– called when a tool call throws, or when a controller returns aResponsewith an error status.reqis as inonExecute, ornullfor a thrown error.toModelOutput– formats the tool result for the model. A formatter that returns anErrorfor a failed call makes the tool throw it.meta?: Record<string, any>– data passed to each call of a controller handler or RPC method; seereq.vovk.meta().
import { deriveTools } from 'vovk';
import { UserRPC } from '@/client';
import TaskController from '@/modules/task/task-controller';
const tools = deriveTools({
meta: { hello: 'world' },
modules: {
UserRPC,
TaskController,
},
toModelOutput: (result) => `Result: ${JSON.stringify(result, null, 2)}`,
onExecute: (result, { name }) => console.log(`${name} executed`, result),
onError: (e) => console.error('Error', e),
});See the Deriving AI Tools guide.
ToModelOutput
Built-in toModelOutput formatters for deriveTools. They shape tool results for LLMs.
ToModelOutput.DEFAULT— the default, used whentoModelOutputisn’t set.ToModelOutput.MCP— formats the output in the MCP tool output shape: text, JSON, image or audio. See tools for details and examples.ToModelOutput.RAW— the result as the procedure gives it; a failed call throws its error, afteronError. See Using with uicast.
createValidateOnClient
Creates the validateOnClient function, which runs client-side validation for RPC methods, for all segments or per segment. It takes a validate function, which gets the input data, the validation schema and more options. validate returns the validated data, or throws an error when validation fails.
import { validateData } from 'validation-library';
import { createValidateOnClient, HttpException, HttpStatus } from 'vovk/create-validate-on-client';
export const validateOnClient = createValidateOnClient({
validate: async (input, schema, meta) => {
const isValid = validateData(input, schema);
if (!isValid) {
throw new HttpException(HttpStatus.NULL, 'Validation failed');
}
return input;
},
});For client-side validation, see the customization page.
procedure
Defines a procedure: a controller handler with validation and schema emission. procedure(options) returns .handle(), which takes the handler, (req, params) => ….
import { z } from 'zod';
import { procedure } from 'vovk';
export default class UserController {
static createUser = procedure({
body: z.object({
name: z.string(),
email: z.string().email(),
}),
output: z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
}),
}).handle((req) => {
// ...
});
}Without .handle(), the procedure throws a Not Implemented error.
procedure options:
body,query,params— input validation schemas.output— the output schema, for JSON responses.iteration— the item schema, for JSON Lines responses.contentType— theContent-Type(a string or an array of strings) the request body may have, for non-JSON bodies such as forms, text or files. SeecontentType.disableServerSideValidation— turns off server-side validation:truefor all parts, or an array of parts.skipSchemaEmission— leaves JSON Schemas out of the emitted schema:truefor all parts, or an array of parts.validateEachIteration— validates every streamed item, not only the first one.operationObject— OpenAPI operation details, for when you can’t use the@operationdecorator.preferTransformed— whetherreq.vovk.*and the response use the transformed values or the raw ones.target— the JSON Schema version the validation library emits for the procedure’s schemas:'draft-2020-12'by default, or another version the library supports, such as'draft-07'.
A procedure also has:
.fn(...)— calls the procedure locally, without HTTP, with the same arguments as an RPC call..schema— the method schema, the same as the RPC method’s..definition— the options passed toprocedure.
See the procedure guide.
progressive
Experimental. Makes one request and gets several responses, each as its own promise.
const { users: usersPromise, tasks: tasksPromise } = progressive(ProgressiveRPC.streamProgressiveResponse);See Progressive Response.
HttpException
An error class that extends the built-in Error, with an HTTP status code and a message. Throw it from a procedure, and Vovk.ts answers with that status code and message.
import { HttpException, HttpStatus } from 'vovk';
throw new HttpException(HttpStatus.BAD_REQUEST, 'Invalid request', { some: 'cause' });The optional third argument holds more data for logging or debugging. The client gets it as error.cause.
See responses.
Inference Types
VovkBody, VovkQuery, VovkParams
Infer the input types of RPC methods and procedures.
import type { VovkBody, VovkQuery, VovkParams } from 'vovk';
import { UserRPC } from '@/client';
type Body = VovkBody<typeof UserRPC.updateUser>;
type Query = VovkQuery<typeof UserRPC.updateUser>;
type Params = VovkParams<typeof UserRPC.updateUser>;See inference.
VovkInput
Infers the params, query and body of a procedure as one object. Use it to type the arguments of a Next.js server action.
import type { VovkInput } from 'vovk';
import type UserController from './user-controller';
type Input = VovkInput<typeof UserController.createUser>;
// { params: VovkParams<...>; query: VovkQuery<...>; body: VovkBody<...> }See inference.
VovkOutput, VovkIteration
Output types of validated procedures:
VovkOutput<T>infers the JSON response type of aprocedure({ output }).VovkIteration<T>infers the JSON Lines item type of aprocedure({ iteration }).
import type { VovkOutput, VovkIteration } from 'vovk';
import { UserRPC, StreamRPC } from '@/client';
type Output = VovkOutput<typeof UserRPC.updateUser>;
type Iteration = VovkIteration<typeof StreamRPC.streamItems>;See inference.
VovkReturnType, VovkYieldType
Infer the return and yield types of a method from its code, when it has no validation schemas.
import type { VovkReturnType, VovkYieldType } from 'vovk';
import { UserRPC, StreamRPC } from '@/client';
type Return = VovkReturnType<typeof UserRPC.updateUser>;
type Yield = VovkYieldType<typeof StreamRPC.streamItems>;See inference.
Other Types and Enums
VovkRequest
A copy of the Next.js NextRequest type, with stricter types for .json() and .nextUrl.searchParams, and a vovk property with input helpers.
VovkRequest doesn’t extend NextRequest, so the vovk package doesn’t depend on the next package.
See req.vovk.
HttpStatus enum
Status codes for throwing and catching server errors. NULL (0) is for an error without an HTTP response, such as a failed client-side validation.
export enum HttpStatus {
NULL = 0,
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLYHINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
AMBIGUOUS = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
REQUESTED_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
I_AM_A_TEAPOT = 418,
MISDIRECTED = 421,
UNPROCESSABLE_ENTITY = 422,
FAILED_DEPENDENCY = 424,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
}VovkSchema
The full schema of the composed client, or of one segment in a segmented client. It has segments, the schema of each segment by name, and an optional meta with the emitted config. To type the schema of one segment, use typeof UserRPC.updateUser.segmentSchema.
See the schema docs.
VovkConfig
The type of the config file.
VovkJSONSchemaBase
A JSON Schema object with type, properties and the other standard keywords.
VovkFetcher
The type of a fetcher, the function that sends the API requests. Exported from vovk/fetcher.
See the fetcher docs.
VovkStreamAsyncIterable
The stream a client method returns for a JSON Lines response: an async iterable of the items, with status, asPromise, onIterate, abortController and abortSilently. Generated clients use it in their method types, OpenAPI mixins included.
VovkValidateOnClient
The type of the client-side validation function that createValidateOnClient creates. Exported from vovk/create-validate-on-client.
See the client-side validation docs.