Skip to content

Code Generation

Generation turns Go procedure registrations into the TypeScript contract consumed by tRPC clients.

Use static analysis before building the frontend:

//go:generate go tool trpcgo generate -o ../web/gen/trpc.ts --zod ../web/gen/zod.ts ./...
Terminal window
go generate ./...

Static analysis reads source packages with go/packages, so it can preserve source-only information that reflection cannot see.

Terminal window
go tool trpcgo generate [flags] [packages]

If no package pattern is provided, trpcgo analyzes ..

FlagDescription
-o, -outputTypeScript output file. Defaults to stdout.
-dirWorking directory for package resolution. Defaults to ..
-w, -watchWatch Go files and regenerate on write/create events.
-zodZod schema output file.
-zod-miniEmit zod/mini functional syntax.

Examples:

Terminal window
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 --zod web/gen/zod.ts -w ./...

The router can write generated files from registered procedure reflection types:

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"),
)
defer router.Close()

When trpc.NewHandler is constructed, trpcgo starts the watcher. It generates once from source, then regenerates when .go files change. If source analysis fails because the Go code is temporarily broken, previous generated files are preserved.

Use WithWatchPackages to avoid watching unrelated frontend or generated directories in larger repositories:

trpcgo.WithWatchPackages("./cmd/api", "./internal/...")
FeatureStatic CLIRuntime reflection
Registered procedure input/output typesYesYes
json, tstype, validate, ts_doc, zod_omit tagsYesYes
Go doc comments as JSDocYesNo
const groups as string/number unionsYesNo
aliases and defined basic typesYesLimited
source-level typed output parser discoveryYesRegistered typed parsers only

Use the CLI for generated files committed or built in CI. Use dev watch for a fast local feedback loop.

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 $Subscription helper aliases only when needed.
  • Nested AppRouterRecord generated from dot-separated procedure paths.
  • Exported AppRouter.
  • RouterInputs and RouterOutputs helpers 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>;
};
};
};

Common Go-to-TypeScript mappings:

GoTypeScript
stringstring
boolboolean
numeric typesnumber
time.Timestring
[]T, [N]TT[]
[]bytestring
map[K]VRecord<K, V>
any, interface{}unknown
json.RawMessageunknown
json.Numbernumber
TrackedEvent[T]T

Pointer fields and fields tagged omitempty or omitzero become optional unless overridden with tstype:",required".

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.
  • 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 or const unions.

Pass --zod or configure WithZodOutput to generate schemas for typed procedure inputs.

Terminal window
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 can still create an empty file.

See Zod Schemas for validate tag mapping, zod/mini, omitempty, dive, cross-field rules, and frontend usage.