# vercel-rpc-cli
[](https://crates.io/crates/vercel-rpc-cli)
[](https://github.com/misha-mad/vercel-rpc/blob/main/LICENSE-MIT)
CLI that scans Rust lambda source files annotated with `#[rpc_query]` /
`#[rpc_mutation]` and generates TypeScript type definitions and a fully typed
RPC client.
Part of the [vercel-rpc](https://github.com/misha-mad/vercel-rpc) project.
## Installation
```bash
cargo install vercel-rpc-cli
```
This installs the `rpc` binary.
## Commands
### `rpc scan`
Parse Rust source files and print discovered procedures, structs, and enums:
```bash
rpc scan --dir api
```
```
Discovered 2 procedure(s), 1 struct(s), 0 enum(s):
Query hello (String) -> String [api/hello.rs]
Query time (()) -> TimeResponse [api/time.rs]
struct TimeResponse {
timestamp: u64,
message: String,
}
```
Also outputs a JSON manifest for tooling consumption.
### `rpc generate`
Generate TypeScript types and a typed client from Rust source files:
```bash
rpc generate \
--dir api \
--output src/lib/rpc-types.ts \
--client-output src/lib/rpc-client.ts \
--types-import ./rpc-types
```
This produces two files:
**`rpc-types.ts`** — TypeScript interfaces and a `Procedures` type map:
```typescript
export interface TimeResponse {
timestamp: number;
message: string;
}
export type Procedures = {
queries: {
hello: { input: string; output: string };
time: { input: void; output: TimeResponse };
};
mutations: {};
};
```
**`rpc-client.ts`** — a typed `RpcClient` with method overloads:
```typescript
export interface RpcClient {
query(key: "time"): Promise<TimeResponse>;
query(key: "hello", input: string): Promise<string>;
}
export function createRpcClient(baseUrl: string): RpcClient;
```
### `rpc watch`
Watch for `.rs` file changes and regenerate automatically (same flags as
`generate`):
```bash
rpc watch --dir api
```
```
vercel-rpc watch mode
api dir: api
types: src/lib/rpc-types.ts
client: src/lib/rpc-client.ts
✓ Generated 2 procedure(s), 1 struct(s) in 3ms
→ src/lib/rpc-types.ts
→ src/lib/rpc-client.ts
Watching for changes in api
```
Changes are debounced at 200 ms. Press Ctrl+C to stop.
## Flags
| `--dir` | `-d` | `api` | Rust source directory to scan |
| `--output` | `-o` | `src/lib/rpc-types.ts` | Output path for TypeScript types |
| `--client-output` | `-c` | `src/lib/rpc-client.ts` | Output path for TypeScript client |
| `--types-import` | | `./rpc-types` | Import path for types in the client file |
## What gets scanned
The parser recognizes:
- **Functions** annotated with `#[rpc_query]` or `#[rpc_mutation]` — extracted
as RPC procedures with their input/output types.
- **Structs** with `#[derive(Serialize)]` — converted to TypeScript interfaces.
- **Enums** with `#[derive(Serialize)]` — converted to TypeScript union types
(unit variants become string literals, tuple/struct variants become tagged
objects).
## Type mapping
| `String`, `&str`, `char` | `string` |
| `i8`..`i128`, `u8`..`u128`, `f32`, `f64` | `number` |
| `bool` | `boolean` |
| `()` | `void` |
| `Vec<T>` | `T[]` |
| `Option<T>` | `T \| null` |
| `HashMap<K, V>`, `BTreeMap<K, V>` | `Record<K, V>` |
| `(A, B, C)` | `[A, B, C]` |
| `Result<T, E>` | `T` (error handled at runtime) |
| Custom structs | `interface` with same fields |
| Enums (unit variants) | `"A" \| "B"` |
| Enums (tuple variants) | `{ A: string } \| { B: number }` |
| Enums (struct variants) | `{ A: { x: number } }` |
## Generated client features
The generated `rpc-client.ts` includes:
- **`RpcClient` interface** with typed overloads for every procedure — full
autocomplete and type checking.
- **`createRpcClient(baseUrl)`** factory function.
- **`RpcError` class** with `status` and `data` fields for structured error
handling.
- **`rpcFetch` helper** — uses `GET` with `?input=<JSON>` for queries and
`POST` with JSON body for mutations. Unwraps the `result.data` envelope
automatically.
## Related crates
- [`vercel-rpc-macro`](https://crates.io/crates/vercel-rpc-macro) — procedural
macros (`#[rpc_query]`, `#[rpc_mutation]`) that generate Vercel lambda
handlers from plain async functions.
## License
MIT OR Apache-2.0