NOTICE: Experimental. We welcome early user feedback.
GoSpeak is a lightweight code generator that exposes your Go methods as REST APIs, enabling remote execution from various languages, including Go and TypeScript. It generates strongly-typed HTTP clients and OpenAPI documentation, offering a streamlined alternative to gRPC for web applications where Go code is your protobuf and JSON is the data format.
Feature | Description |
---|---|
Remote Execution | Invoke Go methods remotely from any language that supports HTTP and JSON. |
Code Generation | Generate strongly-typed HTTP clients in Go, TypeScript, and JavaScript. |
OpenAPI Documentation | Automatically generate OpenAPI 3.x (Swagger) documentation. |
Framework Compatibility | Works with net/http , chi , gin , and echo . The generated http.Handler integrates with existing handlers and middleware. |
Web Compatibility | Uses standard HTTP/HTTPS with JSON, ensuring seamless integration with browsers, HTTP clients, proxies, caches, and tools like cURL . |
GoSpeak uses webrpc to generate REST API client and server code using Go templates. The API routes and JSON payload are defined per webrpc data format and can be exported to OpenAPI (Swagger) documentation.
Language | Code Generation | Requirements |
---|---|---|
Go | Server and Client | Go 1.22+ |
TypeScript | Client | |
JavaScript | Client | ES6 |
Kotlin | Client | coroutines, moshi, ktor |
Dart | Client | Dart 3.1+ |
OpenAPI | Documentation | OpenAPI 3+ (Swagger) |
OpenAPI | Clients | See list of code generators |
- 1. Define service API
- 2. Add webrpc Target Directives
- 3. Generate Code
- 4. Mount the Code-Generated http.Handler
- 5. Implement the Server Business Logic
- 6. Use the Generated Client for Service-To-Service Communication
- 7. Use the Generated Client in Go Tests
package proto
import "context"
type PetStore interface {
GetPet(ctx context.Context, ID int64) (pet *Pet, err error)
ListPets(ctx context.Context) (pets []*Pet, err error)
CreatePet(ctx context.Context, new *Pet) (pet *Pet, err error)
UpdatePet(ctx context.Context, ID int64, update *Pet) (pet *Pet, err error)
DeletePet(ctx context.Context, ID int64) error
}
type Pet struct {
ID int64
Name string
Available bool
PhotoURLs []string
Tags []Tag
}
type Tag struct {
ID int64
Name string
}
The following directives will generate Go server and client code with webrpc:
+//go:webrpc golang -server -pkg=server -out=./server/server.gen.go
+//go:webrpc golang -client -pkg=client -out=./client/example.gen.go
type PetStore interface {
The following will add TypeScript client and OpenAPI 3.x (Swagger) documentation:
//go:webrpc golang -server -pkg=server -out=./server/server.gen.go
//go:webrpc golang -client -pkg=client -out=./client/example.gen.go
+//go:webrpc typescript -client -out=./client/exampleClient.gen.ts
+//go:webrpc openapi -out=./docs/exampleApi.gen.yaml -title=PetStoreAPI
type PetStore interface {
Run gospeak binary to generate webrpc code:
$ gospeak ./proto/api.go
PetStore => ./server/server.gen.go ✓
PetStore => ./client/client.gen.go ✓
PetStore => ./docs/videoApi.gen.yaml ✓
PetStore => ./client/videoDashboardClient.gen.ts ✓
Alternatively, add gospeak as your tool dependency and run it via go generate
:
+//go:generate github.com/golang-cz/gospeak/cmd/gospeak .
package proto
$ go get -tool github.com/golang-cz/gospeak
$ go generate
PetStore => ./server/server.gen.go ✓
PetStore => ./client/client.gen.go ✓
PetStore => ./docs/videoApi.gen.yaml ✓
PetStore => ./client/videoDashboardClient.gen.ts ✓
// cmd/petstore/main.go
package main
import "./server"
func main() {
api := &server.Server{} // your implementation
handler := server.NewPetStoreServer(api)
http.ListenAndServe(":8080", handler)
}
The code generated http.Handler
:
- handles incoming REST API requests
- decodes JSON request body into method argument(s)
- calls your method implementation
- sets proper headers and status code
- encodes method return argument(s) into a JSON response body
What's left for you is the method implementation:
// rpc/server.go
package rpc
type Server struct {
// Dependencies like DB connections, logger, configurations, etc.
}
// rpc/user.go
package rpc
func (s *Server) GetUser(ctx context.Context, uid string) (user *User, err error) {
user, err := s.DB.GetUser(ctx, uid)
if err != nil {
if errors.Is(err, sql.ErrNoRows {
return nil, proto.ErrNotFound.WithCausef("no such user(%q)", uid)
}
return nil, proto.ErrUnexpected.WithCausef("fetch user(%q): %w", uid, err)
}
return user, nil
}
See source code
package main
import "./client"
func main() {
api := client.NewPetStoreClient("http://localhost:8080", http.DefaultClient)
pets, err := api.ListPets(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(pets)
}
package test
import (
"testing"
"./client"
)
func TestAPI(t *testing.T){
api := client.NewPetStoreClient("http://localhost:8080", http.DefaultClient)
pets, err := api.ListPets(ctx)
if err != nil {
t.Fatal(err)
}
t.Log(pets)
}
..and please let us know your thoughts in discussions.