Skip to content
Agentmart
Submit a tool

rpc

Vovk.ts RPC client — how `vovk generate` turns controllers into type-safe client modules, composed vs segmented clients, call shape (`apiRoot`, `params`…

Skill by finom

IndexedMIT★ 54
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/finom-rpc

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Vovk.ts RPC client — how `vovk generate` turns controllers into type-safe client modules, composed vs segmented clients, call shape (`apiRoot`, `params`, `body`, `query`, `meta`, `init`, `disableClientValidation`, `validateOnClient`, `interpretAs`, `transform`, `fetcher`), customizing generation via `outputConfig.imports.fetcher` + `createFetcher` (auth headers, retries, tracing, dynamic `onSuccess` / `onError` subscribers), configuring clients in `vovk.config.mjs` (`composedClient` / `segmentedClient`), error rethrow (`HttpException`, `HttpStatus.NULL`), `VovkInput`/`VovkOutput` against RPC modules, React Query integration (`queryKey`, `streamedQuery`), RPC method surface (`.withDefaults`, `.getURL`, `.queryKey`, `.schema`, `.isRPC`). Use whenever user asks to call API from browser / mobile / other server ("fetch users from the client", "call the API from Next.js client component", "typed API client", "why is my RPC call failing", "add auth token to every request", "custom headers on RPC calls", "retry on 401", "why is the client stale"), regenerate client (`vovk generate`), choose between composed vs segmented clients, or wire up `@/client` imports. Does NOT cover writing procedures / handlers → hand off to `procedure` skill. Does NOT cover segment registration / `initSegment` → hand off to `segment` skill. Does NOT cover JSON Lines streaming clients → hand off to `jsonlines` skill.

Use cases: frontend, backend, security, writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/rpc

What's inside

Skill name
rpc
Description
Vovk.ts RPC client — how `vovk generate` turns controllers into type-safe client modules, composed vs segmented clients, call shape (`apiRoot`, `params`, `body`, `query`, `meta`, `init`, `disableClientValidation`, `validateOnClient`, `interpretAs`, `transform`, `fetcher`), customizing generation via `outputConfig.imports.fetcher` + `createFetcher` (auth headers, retries, tracing, dynamic `onSuccess` / `onError` subscribers), configuring clients in `vovk.config.mjs` (`composedClient` / `segmentedClient`), error rethrow (`HttpException`, `HttpStatus.NULL`), `VovkInput`/`VovkOutput` against RPC modules, React Query integration (`queryKey`, `streamedQuery`), RPC method surface (`.withDefaults`, `.getURL`, `.queryKey`, `.schema`, `.isRPC`). Use whenever user asks to call API from browser / mobile / other server ("fetch users from the client", "call the API from Next.js client component", "typed API client", "why is my RPC call failing", "add auth token to every request", "custom headers on RPC calls", "retry on 401", "why is the client stale"), regenerate client (`vovk generate`), choose between composed vs segmented clients, or wire up `@/client` imports. Does NOT cover writing procedures / handlers → hand off to `procedure` skill. Does NOT cover segment registration / `initSegment` → hand off to `segment` skill. Does NOT cover JSON Lines streaming clients → hand off to `jsonlines` skill.
License
MIT
Instructions
437 lines in SKILL.md · view on GitHub

Agent Skills spec issues

  • description: longer than 1024 characters

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Vovk.ts RPC client

Every controller procedure with HTTP decorator automatically gets typed client counterpart. `vovk generate` produces these client modules from segment schemas, emitting TypeScript into the project:

- **Composed client** (default): one client for all segments at `composedClient.outDir` (default `./src/client`, or `./client` when the app isn't in `src/app`). Import from `'@/client'` (standard Next.js alias; alias-less projects use a relative path).
- **Segmented client**: per-segment subdirs of `segmentedClient.outDir` (same default dir). Import from `'@/client/<segment>'`.

**Key identity**: client module name = **key** used in `initSegment`'s `controllers` map, regardless of import path.

```ts
// server
initSegment({ controllers: { UserRPC: UserController } });

// client (composed)
import { UserRPC } from '@/client';
await UserRPC.getUser({ params: { id: '42' } });
```

Not `UserControllerRPC`. Not `UserController`. Whatever the key is.

## Generating client

```bash
npx vovk generate
```

What it does:

- Reads `.vovk-schema/**/*.json` for every segment (nested segments live in subdirectories, e.g. `.vovk-schema/customer/static.json`).
- Emits generated code into configured `outDir` using selected `fromTemplates` preset (default `['ts']`).
- Composed client (default): emits `index.ts`, `schema.ts`, `openapi.ts`, `openapi.json` at `outDir` root (default `./src/client`, or `./client` when the app isn't in `src/app`). Project's own tsc handles the files; import from `'@/client'`.
- Segmented client: same four files per segment under `<outDir>/<segment>/`; import from `'@/client/<segment>'`.
- Generated dir is gitignored by `vovk init` and rebuilt by the `prebuild` script (`vovk generate`); no need to commit it.

See "Client layout and import paths" below.

**`vovk generate` does NOT rebuild schemas from controller source.** Schema emission = separate step: `vovk dev` runs alongside Next.js dev server, hits each segment's `_schema_` endpoint (only available when `NODE_ENV === 'development'`), writes JSON into `.vovk-schema/`. `vovk generate` reads whatever's there.

Consequence: if backend changed and client looks stale, running `vovk generate` alone isn't enough — `.vovk-schema/` hasn't been refreshed. One shot: `PORT=<port> npx vovk dev --next-dev --exit` starts Next.js, writes `.vovk-schema/` and the client, stops, and exits with 1 on failure. While developing, `npm run dev` keeps both fresh.

When to run:

- **Automatic**: `prebuild` script installed by `vovk init` runs `vovk generate` before `next build`. During `npm run dev`, dev watcher refreshes `.vovk-schema/` on backend changes, client regenerates.
- **Explicit**: before integration tests, after checking out branch, if client method appears missing ("compiled fine, why is `UserRPC.createUser` undefined?"). If method just written is missing, schema probably stale — run `vovk dev --next-dev --exit` (or restart `npm run dev`) rather than re-running `vovk generate`.

## Call shape

Same signature across every RPC method. Core options first, advanced below:

```ts
await ModuleRPC.methodName({
  // data
  params?: { ... },                 // path params — typed
  query?: { ... },                  // query string — typed
  body?: { ... } | FormData | ...,  // narrows by contentType — see "Body by content type"
  meta?: { [key: string]: any },    // serialized as x-meta header — see meta-isolation gotcha

  // transport
  apiRoot?: string,                 // override the generation-time default
  init?: RequestInit,               // headers, credentials, signal, next.revalidate, etc. — see "init" below

  // validation
  disableClientValidation?: boolean,
  validateOnClient?: ...,           // per-call validator override

  // advanced
  interpretAs?: string,             // force response interpretation, e.g. 'application/jsonl' when server didn't set it
  transform?: (data, response) => any, // post-process the parsed response

  // plus any custom options declared via createFetcher<TOptions>({...}) — see Fetcher section
});
```

Return type = `Promise` of whatever procedure's `output` schema resolves to, or whatever `transform` returns if present. Override per call:

```ts
const user = await UserRPC.updateUser<SomeType>({ /* ... */ });
```

### Body by content type

`body`'s accepted type narrows to procedure's `contentType`: `FormData` for `multipart/form-data`, `URLSearchParams` or `FormData` for `application/x-www-form-urlencoded`, `File` / `Blob` / `ArrayBuffer` / `Uint8Array` for binary (`image/*`, `video/*`, `*/*`). Schema-typed objects always accepted. Full matrix → **`procedure`** skill.

```ts
const form = new FormData();
form.append('file', file);
await UserRPC.uploadAvatar({ body: form }); // multipart/form-data procedure
```

Never hand-set `Content-Type` — fetcher derives it (multipart boundary included). Manual headers = usual HTTP 415 trigger.

### `apiRoot`

`apiRoot` **baked in at generation time**: defaults to `/${rootEntry}` (`rootEntry` defaults to `'api'`, so default baked-in value = `/api`). When `outputConfig.origin` is set, bake produces full URL like `http://localhost:3000/api`. Per-call `apiRoot` fully replaces baked-in value for that call — config-level changes still require `vovk generate`.

In browser, relative `/api` resolves against page origin, so same call works from `/dashboard` and `/settings`. On server (Node, integration tests) relative URLs don't resolve — pass full URL per call, bake one in via `outputConfig.origin`, or use `withDefaults({ apiRoot })`.

### `init`

`RequestInit` forwarded to `fetch` — `headers`, `credentials`, `mode`, `cache`, Next.js-specific `next: { revalidate: number }` all pass through.

**`init.signal` aborts the call.** The fetcher ties it to its own per-call `AbortController` (without `AbortSignal.any`, as in React Native and Safari before 17.4, the signal aborts that controller). A pending JSON call rejects with the signal's reason as is (an `AbortError` when `abort()` gets no reason); a stream stops with an error whose `cause` is the reason. A streaming call's async iterable also exposes `.abortController` (and `abortSilently()`) to stop that one stream.

### `transform`

Receives parsed response data and original `Response`. Return anything — including tuple if you want `Response` exposed to caller:

```ts
const [user, response] = await UserRPC.updateUser({
  /* ... */
  transform: (data, response) => [data, response] as const,
});
response satisfies Response;
```

### `interpretAs`

Forces fetcher's content-type dispatch. Useful when server returns JSON Lines but omits `content-type: application/jsonl` (common behind some proxies or when streaming through Next.js): `interpretAs: 'application/jsonl'` makes client treat response as async iterable anyway.

From finom/vovk, MIT.

More from finom/vovk

Signals

Stars
54
Forks
0
Open issues
0
Last commit (this folder)
Oct 13, 2026 (14 days ago)
Last release
Oct 22, 2026 (6 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
Website
vovk.dev
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from finom

Cite this page

rpc skill. Agent Mart. https://agentmart.dev/skills/finom-rpc (last updated 2026-10-24)