Code Generation
Generate TypeScript types from your Go procedures so tRPC clients know which calls are available and what data they accept and return.
Recommended Production Flow
Section titled “Recommended Production Flow”Use static analysis before building the frontend:
//go:generate go tool trpcgo generate -o ../web/gen/trpc.ts --zod ../web/gen/zod.ts ./...go generate ./...Static analysis reads Go source without starting your server. It includes comments, aliases, and const values that runtime reflection cannot recover.
go tool trpcgo generate [flags] [packages]If no package pattern is provided, trpcgo analyzes ..
| Flag | Description |
|---|---|
-o, -output |
TypeScript output file. Defaults to stdout. |
-dir |
Working directory for package resolution. Defaults to .. |
-w, -watch |
Watch Go files and regenerate on write/create events. |
-zod |
Zod schema output file. |
-zod-mini |
Emit zod/mini functional syntax. |
-enums |
Runtime enum value object output file. |
Examples:
go tool trpcgo generate -o web/gen/trpc.ts ./...go tool trpcgo generate -o web/gen/trpc.ts --zod web/gen/zod.ts ./...go tool trpcgo generate -o web/gen/trpc.ts --enums web/gen/enums.ts ./...go tool trpcgo generate -o web/gen/trpc.ts --zod web/gen/zod.ts -w ./...Runtime And Dev Generation
Section titled “Runtime And Dev Generation”After registering your procedures, you can generate files directly from the router using runtime reflection:
if err := router.GenerateTS("web/gen/trpc.ts"); err != nil { return err}
if err := router.GenerateZod("web/gen/zod.ts"); err != nil { return err}In normal development, prefer the integrated watcher:
router := trpcgo.NewRouter( trpcgo.WithDev(true), trpcgo.WithTypeOutput("../web/gen/trpc.ts"), trpcgo.WithZodOutput("../web/gen/zod.ts"), trpcgo.WithEnumsOutput("../web/gen/enums.ts"),)defer router.Close()With WithDev(true) and WithTypeOutput set, constructing trpc.NewHandler starts the watcher. It generates once from source, then regenerates on .go file create/write events. If source analysis fails because the Go code is temporarily broken, previous generated files are preserved. router.Close() stops the watcher.
The watcher uses the same static analysis as the CLI, including const unions and runtime enum objects. GenerateTS and GenerateZod use reflection, which cannot read Go const declarations. To generate enum objects, use WithEnumsOutput with the watcher or --enums with the CLI.
By default, the dev watcher analyzes .. If registrations live in other packages, include them with WithWatchPackages. This also limits which directories are watched:
trpcgo.WithWatchPackages("./cmd/api", "./internal/...")Static Analysis Vs Reflection
Section titled “Static Analysis Vs Reflection”| Feature | CLI / dev watcher | Runtime reflection |
|---|---|---|
| Registered procedure input/output types | Yes | Yes |
json, tstype, validate, ts_doc, zod_omit tags |
Yes | Yes |
| Go doc comments as JSDoc | Yes | No |
| const groups as string/number unions | Yes | No |
runtime enum value objects (enums.ts) |
Yes | No |
| aliases and defined basic types | Yes | Limited |
| generic struct declarations | Generic TypeScript interfaces | Concrete interfaces per instantiation |
| source-level typed output parser discovery | Yes | Registered typed parsers only |
Const groups generate unions for reachable named types declared in the analyzed packages or other packages in the same Go module. Standard-library and third-party constants are ignored, so types like time.Duration still generate as their normal primitive TypeScript shape.
Use the CLI for generated files committed or built in CI. Use dev watch for a fast local feedback loop.
Generated TypeScript Shape
Section titled “Generated TypeScript Shape”Generated trpc.ts includes:
// Code generated by trpcgo. DO NOT EDIT.- Type-only imports from
@trpc/server. - Exported TypeScript definitions for reachable Go types.
$Query,$Mutation, and$Subscriptionhelper aliases only when needed.- Nested
AppRouterRecordgenerated from dot-separated procedure paths. - Exported
AppRouter. RouterInputsandRouterOutputshelpers when procedures exist.
Procedure paths become nested objects:
trpcgo.MustQuery(router, "user.get", getUser)trpcgo.MustMutation(router, "admin.user.ban", banUser)type AppRouterRecord = { user: { get: $Query<GetUserInput, User>; }; admin: { user: { ban: $Mutation<BanUserInput, BanUserResult>; }; };};Type Mapping
Section titled “Type Mapping”Common Go-to-TypeScript mappings:
| Go | TypeScript |
|---|---|
string |
string |
bool |
boolean |
| numeric types | number |
time.Time |
string |
[]T, [N]T |
T[] |
[]byte |
string |
map[K]V |
Record<K, V> |
any, interface{} |
unknown |
json.RawMessage |
unknown |
json.Number |
number |
TrackedEvent[T] subscription item |
{ id: string; data: T } |
For subscriptions, TrackedEvent[T] and *TrackedEvent[T] generate the { id, data } value that httpSubscriptionLink passes to onData. In query results, inputs, or nested fields, TrackedEvent[T] keeps its Go JSON fields: ID, Retry, and Data.
Pointer fields and fields tagged omitempty or omitzero become optional. On named structs, validate:"required" or tstype:",required" makes them required. See Struct Tags for optionality and JSON null handling.
Detection Rules And Limits
Section titled “Detection Rules And Limits”Static generation discovers calls to the top-level registration functions such as Query, Mutation, Subscribe, SubscribeWithFinal, and their Void/Must variants.
Important limits:
- Procedure paths must be string literals for static analysis.
- Packages must load and type-check successfully.
- Only packages matched by the supplied patterns are scanned for registrations. Include subpackages with
./...when needed. - Custom wrapper functions are only detected if the analyzer can see the underlying top-level registration call with a literal path.
- Zod generation targets procedure input types and their dependencies, not output-only types.
- Reflection generation cannot emit source comments, const unions, or the runtime enum value objects derived from them. These need source analysis — the CLI or the dev watcher.
Zod Output
Section titled “Zod Output”Pass --zod or configure WithZodOutput to generate schemas for typed procedure inputs.
go tool trpcgo generate -o web/gen/trpc.ts --zod web/gen/zod.ts ./...If no procedures have typed inputs, runtime GenerateZod and dev watch remove stale Zod files. The CLI writes an empty file at the requested Zod path.
See Zod Schemas for validate tag mapping, zod/mini, omitempty, dive, cross-field rules, and frontend usage.
Enum Values
Section titled “Enum Values”A TypeScript union gives you type checking, but its values are unavailable at runtime. To populate a <select> or check enum membership, pass --enums or configure WithEnumsOutput. This generates an as const object from the same Go constants:
go tool trpcgo generate -o web/gen/trpc.ts --enums web/gen/enums.ts ./...// enums.ts — Code generated by trpcgo. DO NOT EDIT.export const RoleEnum = { viewer: "viewer", admin: "admin", owner: "owner",} as const;The object is keyed by value, so Object.values(RoleEnum) lists the members, Object.hasOwn(RoleEnum, value) tests membership, and RoleEnum.viewer autocompletes. Regenerating updates both the object and the Role union.
Enum objects are written to their own file so you can import trpc.ts using type-only imports. They cover named string enums reachable from procedure inputs or outputs, including enums that never appear in a Zod input schema. Numeric enums are skipped.
If no named string enums are reachable, the CLI and dev watcher write an enums.ts containing only the generated-file header.