Go on the server.
tRPC on the client.

trpcgo keeps your API server in Go and gives frontend teams typed tRPC calls, generated AppRouter contracts, Zod schemas, and runtime enum values.

go get github.com/befabri/trpcgo@latest
server.go
// Role is a user's permission level.
type Role string

const (
  RoleAdmin  Role = "admin"
  RoleEditor Role = "editor"
)

type CreateUserInput struct {
  Name  string `json:"name"`
  Email string `json:"email" validate:"email"`
  Role  Role   `json:"role"`
}

trpcgo.MustMutation(
  router, "user.create", createUser,
)
gen/
/** Role is a user's permission level. */
export type Role = "admin" | "editor";

export interface CreateUserInput {
  name: string;
  email: string;
  role: Role;
}

type AppRouterRecord = {
  user: {
    create: $Mutation<CreateUserInput, User>;
  };
};
client.ts
import type { AppRouter } from "./gen/trpc";
import { RoleEnum } from "./gen/enums";
import {
  CreateUserInputSchema,
} from "./gen/zod";

const client = createTRPCClient<AppRouter>({
  links: [httpBatchLink({ url: "/trpc" })],
});

const input = CreateUserInputSchema.parse({
  name: "Ada",
  email: "ada@example.com",
  role: RoleEnum.admin,
});

const user = await
  client.user.create.mutate(input);

The tRPC backend features you expect, in Go.

Built on plain Go (Golang) and net/http. No TypeScript server required.

Typed procedures
Go queries, mutations, and SSE subscriptions, with middleware, metadata, and server-side callers.
HTTP runtime
Batching, SSE subscriptions, CORS, trusted origins, and error formatting.
Frontend generation
TypeScript AppRouter contracts, Zod schemas, runtime enums, and watch mode.

Start with your Go module.

Point the router at the files you want, register your Go procedures, and the TypeScript client code is written for you.

router := trpcgo.NewRouter(
  trpcgo.WithValidator(validate.Struct),
  trpcgo.WithTypeOutput("../web/gen/trpc.ts"),
  trpcgo.WithZodOutput("../web/gen/zod.ts"),
  trpcgo.WithEnumsOutput("../web/gen/enums.ts"),
)

trpcgo.MustMutation(router, "user.create", createUser)
Open the quick start →