Schema
The npm run dev script runs vovk dev next to next dev with concurrently . The Vovk process watches the modules folder and the segment route files. On the first run and after a change, it calls the _schema_ endpoint of the Next.js dev server (served only when process.env.NODE_ENV is development). It calls the endpoint of the segment whose controller changed, or of every segment when the changed file holds no known controller, such as a service or a validation module. The endpoint returns the schema of its own segment.
Each segment writes its back-end schema to a JSON file of its own, and the client RPC modules are generated from these files. An app with several areas, such as root, admin and customer, has a schema file for each area.
For these segments:
- route.ts (root segment
/api/)
- route.ts (root segment
- route.ts (admin segment
/api/admin)
- route.ts (admin segment
- route.ts (customer segment
/api/customer) - route.ts (static segment
/api/customer/staticfor OpenAPI)
- route.ts (static segment
- route.ts (customer segment
The schema files in .vovk-schema/ are:
- root.json
- admin.json
- customer.json
- static.json
- _meta.json
The files follow the segment tree: a foo/bar/baz segment emits .vovk-schema/foo/bar/baz.json. Only the root segment is different: its file is root.json, for clarity.
_meta.json holds more metadata, such as the selected fields of vovk.config under the config key.
The CLI reads these files into one object with segments and meta. segments is flat, keyed by segment name; meta holds the content of _meta.json. In the project’s own schema folder, config comes from the current vovk.config, with only the keys it exposes now, so vovk generate doesn’t wait for vovk dev to update the file. The client and the OpenAPI output are generated from this object.
{
"segments": {
"": { ... }, // root segment schema from root.json
"admin": { ... }, // admin segment schema from admin.json
"customer": { ... }, // customer segment schema from customer.json
"customer/static": { ... } // static segment schema from customer/static.json
},
"meta": { ... } // content of _meta.json file
}The client exports it as schema.
import { schema } from '@/client';
// or
import { schema } from '@/client/schema'; // exports only the schema object (no RPC modules)
console.log(schema); // full schema
console.log(schema.segments.admin); // admin segment schemaThe schema object is typed from its JSON files, where every string field is a plain string. So it isn’t assignable to the VovkSchema type, whose fields are literal unions.
The segmented client exports schema too, with exactly one segment:
{
"segments": {
"": { ... } // root segment schema from root.json
},
"meta": { ... } // content of _meta.json file
}import { schema } from '@/client/root';
// or
import { schema } from '@/client/root/schema';A segment schema file:
{
// Segment schema version
"$schema": "https://vovk.dev/api/schema/v3/segment.json",
// The initSegment option that sets whether the segment emits its schema
"emitSchema": true,
// Segment name, an empty string for the root segment
"segmentName": "foo",
// "segment", or "mixin" for an OpenAPI mixin
"segmentType": "segment",
// Controllers by name, for fast access
// The key is the name that "@/client" exports
// The value describes the controller
"controllers": {
"HelloRPC": {
// RPC module name in the client
"rpcModuleName": "HelloRPC",
// Controller class name; "vovk dev" uses it to find the segment of a changed controller
"originalControllerName": "HelloController",
// The argument of the @prefix class decorator
"prefix": "hello",
// Handlers by name, for fast access
// The key is the static method name
// The value describes the handler
"handlers": {
"getHello": {
// Endpoint path, appended to the prefix
"path": "greeting",
// HTTP method
"httpMethod": "POST",
// Validation for "body", "query", "params", "output" and "iteration"
"validation": {
"body": {
"type": "object",
"properties": {
"foo": {
"type": "string"
}
},
"required": ["foo"],
"additionalProperties": false,
// A procedure with a `contentType` option also gets "x-contentType" here,
// the list of accepted types, such as ["multipart/form-data"]; a JSON body has none
"$schema": "https://json-schema.org/draft/2020-12/schema"
},
"query": {
"type": "object",
"properties": {
"bar": {
"type": "string"
}
},
"required": ["bar"],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2020-12/schema"
},
"params": {
"type": "object",
"properties": {
"baz": {
"type": "string"
}
},
"required": ["baz"],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2020-12/schema"
},
"output": {
// or "iteration" for a JSON Lines response
"type": "object",
"properties": {
"hello": {
"type": "string"
}
},
"required": ["hello"],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
},
// OpenAPI operation, for OpenAPI documentation, LLM tools, MCP and so on
"operationObject": {
"summary": "Hello world",
"description": "Hello world",
// Custom field for AI tools
"x-tool": {
"name": "getHello", // custom name for an AI tool
"title": "Get Hello", // custom title for an AI tool
"description": "Hello world", // custom description for an AI tool
"hidden": false, // whether to leave the tool out of the derived tools
"meta": { "category": "greetings" } // static data copied to the derived tool
}
},
// Custom data, set by a custom decorator
"misc": {
"hello": "World"
}
}
}
}
}
}