Files

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