5.6 KiB
Getting Started
This guide walks through building a small service end to end: install the CLI, define a Go service, configure code generation, generate the bindings, wire up the server, and call it from a generated TypeScript client.
Install the CLI
Pick whichever method fits your toolchain.
::: code-group
go install github.com/foomo/gotsrpc@latest
brew install foomo/tap/gotsrpc
mise use github.com:foomo/gotsrpc
:::
You can also download a prebuilt binary from the GitHub releases page.
Verify the installation:
gotsrpc version
1. Define a service
A gotsrpc service is a plain Go interface plus a struct that implements
it. Create service/service.go:
package service
type Service interface {
Hello(name string) string
}
And an implementation in service/handler.go:
package service
import "fmt"
type Handler struct{}
func (h *Handler) Hello(name string) string {
fmt.Println(name)
return name
}
Arguments and return values must be serializable (they are marshalled, typically
as JSON). See Writing Services for the full range of
supported method signatures — including context.Context, direct
http.ResponseWriter/*http.Request access, and error returns.
2. Configure code generation
Create a gotsrpc.yml next to your module. It points gotsrpc at the Go package
to scan, maps HTTP routes to services, and says where to write the generated
TypeScript.
# yaml-language-server: $schema=gotsrpc.schema.json
module:
name: github.com/foomo/gotsrpc/v3 # your Go module name
path: ../../ # path to the module root (has go.mod)
targets:
basic:
services:
/service: Service # HTTP route -> Go service name
package: github.com/foomo/gotsrpc/v3/example/basic/service
out: ./client/src/service-client.ts # generated TypeScript client
gorpc: # (optional) also generate a Go<->Go client
- Service
tsrpc: # generate a TypeScript client
- Service
mappings:
github.com/foomo/gotsrpc/v3/example/basic/service:
out: ./client/src/service-vo.ts # generated TypeScript type definitions
The first-line # yaml-language-server: $schema=... comment enables
autocompletion and validation in editors. Every field is documented in the
Configuration reference.
3. Generate
Run the CLI, pointing it at your config file:
gotsrpc gotsrpc.yml
This is shorthand for gotsrpc generate gotsrpc.yml. gotsrpc writes:
| File | What it is |
|---|---|
gotsrpc_gen.go |
HTTP service proxy (server side) |
gotsrpcclient_gen.go |
HTTP client (Go side) |
gorpc_gen.go / gorpcclient_gen.go |
Go binary-protocol proxy/client (only when gorpc: is set) |
service-client.ts |
TypeScript client class |
service-vo.ts |
TypeScript type definitions |
::: warning
Generated files are overwritten on every run and are marked
// Code generated by gotsrpc ... DO NOT EDIT. — never edit them by hand.
Obsolete generated files are not deleted automatically, so add a clean step
to your build if you rename or remove services.
:::
The referenced Go code must compile and every referenced package must have a mapping configured, otherwise generation fails.
4. Serve the proxy
The generated proxy is an http.Handler. Construct it with your implementation
and mount it on a route prefix:
package main
import (
"net/http"
"strings"
"github.com/foomo/gotsrpc/v3/example/basic/service"
)
func main() {
ws := service.NewDefaultServiceGoTSRPCProxy(&service.Handler{})
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/service/") {
ws.ServeHTTP(w, r)
return
}
http.NotFound(w, r)
})
panic(http.ListenAndServe("localhost:3000", mux))
}
5. Call it from TypeScript
The generated client does not ship a network layer — you supply a transport,
a small function that POSTs the argument array to <endpoint>/<Method> and
returns the parsed response. This keeps headers, cookies, error handling and
environment differences (browser vs. Node.js) under your control.
// transport.ts
const transport = (endpoint: string) => async <T>(method: string, args: any = []) => {
const response = await fetch(`${endpoint}/${encodeURIComponent(method)}`, {
method: "POST",
body: JSON.stringify(args),
});
return (await response.json()) as T;
};
export default transport;
// app.ts
import { ServiceClient } from "./service-client.js";
import transport from "./transport.js";
const client = new ServiceClient(transport("/service"));
client.hello("World").then((res) => console.log(res)); // "World"
Method names are camelCased and return values come back positionally. Pointer
returns become T | null in TypeScript.
Calling from Go
If you enabled gorpc: (or just want the Go HTTP client), the generated Go
client mirrors the interface — every method takes a leading context.Context
and returns a trailing error:
c := service.NewServiceGoTSRPCClientWithClient("http://127.0.0.1:3000", "/service", httpClient)
res, err := c.Hello(ctx, "Hello World")
Next steps
- Writing Services — supported method signatures, errors, and types
- Configuration — every
gotsrpc.ymlfield and the JSON schema - CLI reference — commands, flags, and environment variables