Type-safe Go backend for web apps.
Write Go functions, call them from TypeScript with full type safety. No IDL required.
Write Go types and handlers:
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email" validate:"required,email"`
}
type GetUserRequest struct {
ID int64 `json:"id"`
}
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2"`
Email string `json:"email" validate:"required,email"`
}func GetUser(ctx context.Context, req *GetUserRequest) (*User, error) {
// ...
}
func CreateUser(ctx context.Context, req *CreateUserRequest) (*User, error) {
// ...
}tygor generates TypeScript types:
// Code generated by tygor. DO NOT EDIT.
export interface User {
id: number;
name: string;
email: string;
}Call your API with full type safety:
const user = await client.Users.Get({ id: "123" });
// user: User (autocomplete works)Clone the Solid.js + Vite starter and start building:
bunx degit ahimsalabs/tygor-templates/starter-solid my-app
cd my-app && bun i && bun devOr React: bunx degit ahimsalabs/tygor-templates/starter-react my-app
Prerequisites: Go 1.21+, Node.js 18+
If you prefer to start from scratch rather than the example above:
The CLI finds your app by scanning for any exported function that returns *tygor.App:
func SetupApp() *tygor.App {
app := tygor.NewApp()
users := app.Service("Users")
users.Register("Get", tygor.Query(GetUser)) // GET request
users.Register("Create", tygor.Exec(CreateUser)) // POST request
return app
}- Query handlers serve GET requests (query params in URL)
- Exec handlers serve POST requests (JSON body)
app := SetupApp()
http.ListenAndServe(":8080", app.Handler())# Generate types and manifest
tygor gen ./src/rpc
# With Zod schemas for client-side validation
tygor gen ./src/rpc --flavor zodThis creates:
types.ts- TypeScript interfaces for all your Go typesmanifest.ts- Service registry for the clientschemas.zod.ts- Zod schemas (with--flavor zod)
import { createClient } from "@tygor/client";
import { registry } from "./rpc/manifest";
const client = createClient(registry, {
baseUrl: "http://localhost:8080",
});
// Type-safe API calls
const user = await client.Users.Get({ id: 123 });
console.log(user.name); // autocomplete worksThe Vite plugin gives you hot reload, error overlay, and devtools:
npm install @tygor/vite-pluginimport { defineConfig } from "vite";
import { tygor } from "@tygor/vite-plugin";
export default defineConfig({
plugins: [
tygor({
workdir: "../server", // Path to your Go module
build: "go build -o ./tmp/server .",
start: (port) => ({ cmd: `./tmp/server -port=${port}` }),
}),
],
});The plugin automatically:
- Runs
tygor genon startup and file changes - Hot-reloads your Go server with zero downtime
- Shows build errors in-browser
- Provides a devtools sidebar with API info
Your frontend state persists across server reloads. See @tygor/vite-plugin for full docs.
- No IDL required. Go structs are your schema. Or use protobuf if you prefer schema-first.
- Standard HTTP/JSON. Debuggable with curl. Cacheable. Works with your existing infra.
- Tiny client. Proxy-based, <3KB. No per-endpoint generated code.
- Go-native. Works with
net/http, your middleware, your patterns.
Use tygor if you:
- Build fullstack apps with Go backend + TypeScript frontend
- Work in a monorepo (or want types to stay in sync)
- Prefer iterating on code over maintaining schema files
- Would use tRPC if your backend was TypeScript
Consider alternatives if you:
- Need multi-language clients → OpenAPI generators
- Need strict public API contracts → Connect/gRPC with protobuf
Structured error codes that map to HTTP status:
return nil, tygor.NewError(tygor.CodeNotFound, "user not found")
// → HTTP 404, JSON: {"code": "not_found", "message": "user not found"}Validation errors return CodeInvalidArgument automatically when validate tags fail.
Server-side validation with validator/v10 tags, client-side with generated Zod schemas:
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2"`
Email string `json:"email" validate:"required,email"`
}See examples/zod for client-side validation with Zod.
Cache control for Query (GET) endpoints:
users.Register("Get", tygor.Query(GetUser).
CacheControl(tygor.CacheConfig{MaxAge: 5 * time.Minute, Public: true}))Cross-cutting concerns at app, service, or handler level:
app.WithUnaryInterceptor(loggingInterceptor)
service.WithUnaryInterceptor(authInterceptor)
handler.WithUnaryInterceptor(auditInterceptor)Execution order: app → service → handler → your function.
Standard HTTP middleware:
app.WithMiddleware(middleware.CORS(middleware.CORSAllowAll))tygor gen <outdir> # Generate TypeScript types and manifest
-p, --package Package to scan (default: current directory)
-f, --flavor Add Zod schemas: zod, zod-mini
-d, --discovery Generate discovery.json for runtime introspection
-c, --check Check if files are up-to-date (for CI)
tygor check # Validate exports without generating
tygor dev # Start devtools server (used by Vite plugin)
--rpc-dir Directory with discovery.json
--port Server port (default: 9000)You can also generate types programmatically, useful for custom build scripts:
tygorgen.FromApp(app).ToDir("./client/src/rpc")| Example | Description |
|---|---|
| react | React + Vite starter with type-safe API calls |
| zod | Client-side validation from Go validate tags |
| newsserver | Simple CRUD API to explore the basics |
See all examples including auth, protobuf, and more.
Important
tygor is pre-release. The API and protocol may change.
Pin @tygor/client and tygor.dev to the same version.
MIT
Tiger image by Yan Liu, licensed under CC-BY.