https://github.com/varavelio/vdl-plugin-rpc-ts
Plugin to generate VDL RPC for TypeScript
https://github.com/varavelio/vdl-plugin-rpc-ts
api codegen rpc ts typescript varavel vdl vdl-plugin vdl-rpc
Last synced: 9 days ago
JSON representation
Plugin to generate VDL RPC for TypeScript
- Host: GitHub
- URL: https://github.com/varavelio/vdl-plugin-rpc-ts
- Owner: varavelio
- License: mit
- Created: 2026-03-31T00:35:43.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-01T15:31:03.000Z (3 months ago)
- Last Synced: 2026-04-01T20:32:23.612Z (3 months ago)
- Topics: api, codegen, rpc, ts, typescript, varavel, vdl, vdl-plugin, vdl-rpc
- Language: TypeScript
- Homepage:
- Size: 186 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
VDL RPC TypeScript Plugin
Generate TypeScript RPC clients and RPC servers from annotation-based VDL services.
This plugin is RPC-only.
It does not generate business types, enums, or constants. Those come from [`varavelio/vdl-plugin-ts`](https://github.com/varavelio/vdl-plugin-ts), and this plugin references them through `typesImport`.
All examples below use `varavelio/vdl-plugin-rpc-ts@v0.1.0`.
## Quick Start
1. Generate TypeScript models with `varavelio/vdl-plugin-ts`.
2. Generate RPC client (`target "client"`) and/or RPC server (`target "server"`).
3. Run your normal VDL generation command (for example: `vdl generate`).
```vdl
const config = {
version 1
plugins [
{
src "varavelio/vdl-plugin-ts@v0.1.4"
schema "./schema.vdl"
outDir "./generated/types"
options {
importExtension "js"
}
}
{
src "varavelio/vdl-plugin-rpc-ts@v0.1.0"
schema "./schema.vdl"
outDir "./generated/client"
options {
target "client"
typesImport "../types/index.js"
importExtension "js"
}
}
{
src "varavelio/vdl-plugin-rpc-ts@v0.1.0"
schema "./schema.vdl"
outDir "./generated/server"
options {
target "server"
typesImport "../types/index.js"
importExtension "js"
}
}
]
}
```
If you only need one side, keep only one RPC plugin block.
## Plugin Options
| Option | Type | Required | Default | What it changes |
| ----------------- | ------------------------ | -------- | ------- | ----------------------------------------------------------------------------------------------------- |
| `typesImport` | `string` | yes | - | Import path pointing to the output generated by `varavelio/vdl-plugin-ts` (used exactly as provided). |
| `target` | `"client" \| "server"` | yes | - | Selects what to generate in that invocation. The plugin generates one target at a time. |
| `importExtension` | `"none" \| "js" \| "ts"` | no | `"js"` | Controls internal imports between generated RPC files. It does not rewrite `typesImport`. |
## Generated Files
For `target "client"`:
- `client.ts`
For `target "server"`:
- `server.ts`
- `adapters/node.ts`
- `adapters/fetch.ts`
If your schema has no discovered `@rpc`, `@proc` or `@stream` operations, no files are emitted.
## What You Get
For `target "client"`:
- `NewClient(baseURL).build()` to create a typed client.
- Flattened procedure and stream builders under `client.procs.*()` and `client.streams.*()`.
- Global and operation-level headers (static or dynamic providers).
- Interceptors.
- Retry/timeout for procedures and reconnect policies for streams.
- Stream lifecycle hooks and maximum message size controls.
For `target "server"`:
- `new Server()` with typed request context.
- Typed registration APIs for procedures and streams.
- Middleware at global, RPC, procedure, stream, and stream-emit levels.
- Global and RPC-level error handlers.
- Global, RPC-level, and stream-level ping configuration.
- `createNodeHandler(...)` and `createFetchHandler(...)` adapters for HTTP runtimes.
Both targets include runtime catalogs:
- `VDLPaths`
- `VDLProcedures`
- `VDLStreams`
Non-marker annotations are preserved in operation metadata.
## RPC Annotation Model
This plugin follows the VDL annotation model:
- `@rpc` marks a top-level type as an RPC service.
- `@proc` marks a field as a request-response operation.
- `@stream` marks a field as a server-streaming operation.
```vdl
@rpc
type Messages {
@proc
send {
input {
roomId string
text string
}
output {
accepted bool
}
}
@stream
events {
input {
roomId string
}
output {
text string
}
}
}
```
## Usage Example
### Client
```ts
import { NewClient } from "./generated/client/client";
const client = NewClient("http://localhost:3000/rpc")
.withGlobalHeader("authorization", "Bearer ")
.build();
const procOutput = await client.procs.messagesSend().execute({
roomId: "room-1",
text: "hello",
});
console.log(procOutput.accepted);
const { stream, cancel } = client.streams.messagesEvents().execute({
roomId: "room-1",
});
for await (const event of stream) {
if (!event.ok) {
console.error(event.error);
break;
}
console.log(event.output.text);
}
cancel();
```
### Server (Node.js adapter)
```ts
import { createServer } from "node:http";
import { Server } from "./generated/server/server";
import { createNodeHandler } from "./generated/server/adapters/node";
type Context = { requestId: string };
const rpcServer = new Server();
rpcServer.rpcs
.messages()
.procs.send()
.handle(async (_ctx) => {
return { accepted: true };
});
rpcServer.rpcs
.messages()
.streams.events()
.handle(async (ctx, emit) => {
await emit(ctx, { text: `joined ${ctx.input.roomId}` });
});
const handler = createNodeHandler(rpcServer, () => ({ requestId: "req-1" }), {
prefix: "/rpc",
});
createServer(async (req, res) => {
await handler(req, res);
}).listen(3000);
```
### Server (Fetch-compatible adapter)
```ts
import { Server } from "./generated/server/server";
import { createFetchHandler } from "./generated/server/adapters/fetch";
const rpcServer = new Server();
export default {
fetch: createFetchHandler(rpcServer, undefined, { prefix: "/rpc" }),
};
```
## Important Notes
- `baseURL` in `NewClient(baseURL)` should point to your RPC prefix (for example: `https://api.example.com/rpc`).
- The generated client appends `/{rpcName}/{operationName}` automatically.
- `typesImport` must point to the generated output from `vdl-plugin-ts`.
- To generate both client and server, run this plugin twice (one block per `target`).
## License
This plugin is released under the MIT License. See [LICENSE](LICENSE).