Skip to content

Expose any Go interface as a REST API – with OpenAPI docs and type-safe clients for Go, TypeScript, and more. Fast, lightweight, no magic.

License

Notifications You must be signed in to change notification settings

golang-cz/gospeak

Repository files navigation

NOTICE: Experimental. We welcome early user feedback.

GoSpeak

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.

Language support

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

Quick example

1. Define service API

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
}

2. Add webrpc Target Directives

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 {

3. Generate Code

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 ✓

4. Mount the Code-Generated http.Handler

// cmd/petstore/main.go
package main

import "./server"

func main() {
	api := &server.Server{} // your implementation

	handler := server.NewPetStoreServer(api)

	http.ListenAndServe(":8080", handler)
}

5. Implement the Server Business Logic

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

6. Use the Generated Client for Service-To-Service Communication

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)
}

7. Use the Generated Client in Go Tests

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)
}

Enjoy!

..and please let us know your thoughts in discussions.

Authors

License

MIT license

About

Expose any Go interface as a REST API – with OpenAPI docs and type-safe clients for Go, TypeScript, and more. Fast, lightweight, no magic.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Languages