# Webrpc Ridl Schema

> Write and edit webrpc .ridl schema files and run webrpc-gen code generation. Use when working with RIDL syntax, webrpc schemas, generated *.gen.go / *.gen.ts files, or designing webrpc API contracts (Go/TypeScript/JavaScript/Kotlin/Dart/OpenAPI).

- Skill: `webrpc/webrpc-ridl-schema` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add webrpc/webrpc-ridl-schema`
- Raw SKILL.md: https://api.skillmd.com/api/skills/webrpc/webrpc-ridl-schema/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: webrpc (https://skillmd.com/u/webrpc)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/webrpc/webrpc-ridl-schema

---


# webrpc RIDL schemas and codegen

RIDL defines an HTTP API contract; `webrpc-gen` generates typed servers and clients
from it. POST-only with JSON bodies — never design path or query params.

## Rules
- Edit `.ridl` sources, never `*.gen.*` output — rerun codegen instead.
- Identifiers are case-sensitive. Run `ridlfmt` after edits. No circular imports.
- Prefer succinct method signatures (one request struct in, one response struct out);
  never mix succinct and multi-arg forms in one method.
- Common mistakes: missing `webrpc = v1`; deprecated `message` keyword (use `struct`);
  forgetting `?` on nullable fields; omitting `HTTP <status>` on non-400 errors.

## Header (required) and imports
```
webrpc = v1
name = <schema-name>
version = <schema-version>
basepath = /rpc

import "path/to/file.ridl"    # everything (quotes optional)
import "../shared/ids.ridl"   # named members only
  - OrgID
```
Imports merge into one flat schema. An import with no referenced member can be
intentional (pulls types into the generated package) — keep those in codegen roots only.

## Running codegen
Install from releases, or pin as a Go tool dependency and run via
`go run github.com/webrpc/webrpc/cmd/webrpc-gen`:
```
webrpc-gen -schema=main.ridl -target=golang -pkg=proto -server -client -out=./proto/api.gen.go
webrpc-gen -schema=app/dashboard.ridl -target=typescript -client -enumStyle=union -out=./src/rpc/proto/api.gen.ts
```
Golang flags: `-errorStackTrace=true`, `-json=sonic`. Pin targets: `-target=golang@v0.14.0`.

## Multi-file layout (recommended for larger projects)
```
schema/
  main.ridl          # Go codegen root: imports errors.ridl + every app/*.ridl
  errors.ridl        # all errors in one place
  app/<app>.ridl     # per-app TS codegen roots
  service/<app>/     # one service block per domain
  model/             # shared structs, typed ids, enums
```
Go codegen runs on `main.ridl` (one unified package); TS codegen on `app/<app>.ridl`
so each webapp sees only its own surface.

## Types
`#` starts a comment; adjacent comments attach to the next definition as docs.
Core: `byte`, `bool`, `any`, `null`, `string`, `timestamp`, `uint8`-`64`, `int8`-`64`,
`float32/64`, `[]T`, `map<K,V>`.
```
struct User
  - id: uint64
  - name?: string    # optional; fields are required unless marked ?

enum SortOrder: uint32   # values default by index
  - DESC
  - ASC

type WebhookID: string          # alias: JSON stays string;
  + go.alias = typeid.UUID      # Go uses this type (typed IDs, uuid.UUID, ...)
  + go.type.import = github.com/example/typeid
```

## Services, errors, annotations
```
error 1000 Unauthorized "unauthorized" HTTP 401    # status defaults to 400
error 4001 InvalidTransition "invalid state" HTTP 409

service Example
  @deprecated:"use NewMethod instead"
  @access:s2s
  - GetUser(GetUserRequest) => (GetUserResponse)   # succinct form, preferred
  - Ping() => (version: string)                    # no-payload inline return is fine
```
- Group error codes by range (1xxx auth, 2xxx validation, 3xxx not-found, 4xxx business).
- Go: a `WebRPCError` must reach the transport unwrapped (the server type-asserts it) —
  use `proto.ErrXxx.WithCause(err)`, not `fmt.Errorf` chains.
- Custom annotations (`@access:`, `@perms:`) are readable at runtime: middleware calls
  `proto.MethodCtx(ctx)` then `Annotation("access")` / `HasAnnotation(...)`.

## Field metadata
```
  - id: string
    + go.field.name = ID       # rename the Go field
    + go.field.type = uint64   # override the Go type
    + go.tag.db = id           # any go.tag.<name> becomes a struct tag
  - featureIndex: int
    + json = -                 # server-only: dropped from Go json AND the TS interface
```
- `+ json = -` vs `+ go.tag.json = -`: the latter is Go-only — the field stays in the TS
  interface but is always `undefined` at runtime (the TS type lies). Use `go.tag.json`
  only for Go tag options like `,omitempty`.
- The generator title-cases names naively (`userId` → `UserId`); fix acronyms with
  `go.field.name` (`ID`, `URL`, `URI`, `API`, `JSON`, `HTTP`, `RPC`, `IP`, `DB`, ...).

### Inferred Go types — override only to get a *different* type
| RIDL | Go |
|---|---|
| primitives, `[]T`, `map<K,V>` | direct equivalents; `timestamp` → `time.Time` |
| `name?: <simple>` | pointer (`*string`); `name?: []T` stays `[]T` (already nil-able) |
| struct fields | pointers: `*S`, `[]*S`, `map[K]*S` (succinct method arg: value) |
| enum / alias | value; pointer when optional |

Useful overrides: a different type (`json.RawMessage`, typed IDs), value-element slices
(`[]S`), non-pointer optionals. Overrides matching the table are noise — delete them.

## Custom templates
`-target` accepts a local Go text/template directory, and fields carry arbitrary
metadata — combine them to generate validators, permission constants, etc.:
```
  - email: string
    + validate = "required,maxlen=320,email"
```
```
webrpc-gen -schema=main.ridl -target=./schema/templates/validate-go -pkg=proto -out=./proto/validate.gen.go
```

## References
`_examples/golang-basics/example.ridl`, `schema/README.md`, `schema/ridl/README.md`.
Official targets: `gen/{golang,typescript,javascript,kotlin,dart,openapi}` (embedded in `webrpc-gen`).

