gospeak

package module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2024 License: MIT Imports: 12 Imported by: 0

README

GoSpeak - Generate REST APIs from Go code

NOTICE: Under development. Seeking early user feedback.

GoSpeak is a simple RPC framework, a lightweight alternative to gRPC and Twirp, where Go code is your protobuf.

//go:generate github.com/golang-cz/gospeak/cmd/gospeak ./
package proto

//go:webrpc golang -server -pkg=server -out=./server/server.gen.go
//go:webrpc golang -client -pkg=client -out=./client/example.gen.go
type ExampleAPI interface {
	Ping(context.Context, *Ping) (*Pong, error)
}

Usage:

$ go get github.com/golang-cz/gospeak/cmd/gospeak@latest
$ go generate
            ExampleAPI => ./server/server.gen.go ✓
            ExampleAPI => ./client/example.gen.go ✓

Language support

GoSpeak uses webrpc-gen tool to generate REST API client & server code using Go templates. The API routes and JSON payload are defined per webrpc specs and can be exported to OpenAPI 3.x (Swagger) documentation.

Server Client
Go 1.20+ <=> Go 1.17+
Go 1.20+ <=> TypeScript
Go 1.20+ <=> JavaScript (ES6)
Go 1.20+ <=> OpenAPI 3+ (Swagger documentation)
Go 1.20+ <=> Any OpenAPI client code generator

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 target language directives

Generate Go server and Go client code with go:webrpc directives:

+//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 {

Generate 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

Install gospeak and generate the 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 ✓

NOTE: Alternatively, you can go get github.com/golang-cz/gospeak as your dependency and run go generate against //go:generate github.com/golang-cz/gospeak/cmd/gospeak . directive.

4. Mount the API server

// cmd/petstore/main.go
package main

import "./server"

func main() {
	api := &server.Server{} // implements PetStore interface{}

	handler := server.NewPetStoreServer(api)
	http.ListenAndServe(":8080", handler)
}

5. Implement the server business logic

The generated server code already

  • handles incoming REST API requests
  • unmarshals JSON request into method argument(s)
  • calls your RPC method implementation, ie. `server.GetPet(ctx, 1)``
  • marshals return argument(s) into a JSON response

What's left is the business logic. Implement the interface methods:

// rpc/server.go
package rpc

type Server struct {
	/* DB connection, config 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, io.EOF) {
			return nil, Errorf(ErrNotFound, "failed to find user(%v)", uid)
		}
		return nil, WrapError(ErrInternal, err, "failed to fetch user(%v)", uid)
	}

	return user, nil
}

See source code

6. Use the generated client

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. Test your API

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 let us know what you think in discussions.

Authors

License

MIT license

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrWebrpcEndpoint      = WebRPCError{Code: 0, Name: "WebrpcEndpoint", Message: "endpoint error", HTTPStatus: 400}
	ErrWebrpcRequestFailed = WebRPCError{Code: -1, Name: "WebrpcRequestFailed", Message: "request failed", HTTPStatus: 400}
	ErrWebrpcBadRoute      = WebRPCError{Code: -2, Name: "WebrpcBadRoute", Message: "bad route", HTTPStatus: 404}
	ErrWebrpcBadMethod     = WebRPCError{Code: -3, Name: "WebrpcBadMethod", Message: "bad method", HTTPStatus: 405}
	ErrWebrpcBadRequest    = WebRPCError{Code: -4, Name: "WebrpcBadRequest", Message: "bad request", HTTPStatus: 400}
	ErrWebrpcBadResponse   = WebRPCError{Code: -5, Name: "WebrpcBadResponse", Message: "bad response", HTTPStatus: 500}
	ErrWebrpcServerPanic   = WebRPCError{Code: -6, Name: "WebrpcServerPanic", Message: "server panic", HTTPStatus: 500}
	ErrWebrpcInternalError = WebRPCError{Code: -7, Name: "WebrpcInternalError", Message: "internal error", HTTPStatus: 500}
)

Webrpc errors

Functions

This section is empty.

Types

type Target added in v0.1.0

type Target struct {
	Schema        *schema.WebRPCSchema
	Generator     string
	InterfaceName string
	OutFile       string
	Opts          map[string]interface{}
}

func CollectInterfaces added in v0.2.0

func CollectInterfaces(pkg *packages.Package) ([]*Target, error)

Find all Go interfaces with the special //go:webrpc comments.

func Parse

func Parse(filePath string) ([]*Target, error)

Parse Go source file or package folder and return WebRPC schema.

type WebRPCError deprecated added in v0.6.3

type WebRPCError struct {
	Name       string `json:"error"`
	Code       int    `json:"code"`
	Message    string `json:"msg"`
	Cause      string `json:"cause,omitempty"`
	HTTPStatus int    `json:"status"`
	// contains filtered or unexported fields
}

Deprecated: This type may be removed in the future.

func (WebRPCError) Error added in v0.6.3

func (e WebRPCError) Error() string

func (WebRPCError) Is added in v0.6.3

func (e WebRPCError) Is(target error) bool

func (WebRPCError) StackFrames added in v0.7.5

func (e WebRPCError) StackFrames() []uintptr

func (WebRPCError) Unwrap added in v0.6.3

func (e WebRPCError) Unwrap() error

func (WebRPCError) WithCause added in v0.7.5

func (e WebRPCError) WithCause(cause error) WebRPCError

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL