go_nats

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Sep 20, 2024 License: AGPL-3.0 Imports: 4 Imported by: 0

README

protoc-gen-go-nats

This is a protoc plugin that generates Go server and client code for NATS microservices.

Prior experience with Protobuf is greatly recommended, especially to understand how the package and imports work.

Installation

You already need to have the protoc compiler along the Go protobuf plugin installed on your system. After that, you can go ahead and install this plugin using the following command:

go install xiam.li/go-nats/cmd/protoc-gen-go-nats@latest

To check if the installation was successful, you can run:

protoc-gen-go-nats -v

Usage

Upon installation, you should create a protobuf file that contains a service, similar to how gRPC servers work. An example protobuf file might look like this:

syntax = "proto3";
package your.package;
option go_package = "github.com/user/repo/pb;pb";

service HelloWorldService {
    rpc HelloWorld(HelloWorldRequest) returns (HelloWorldResponse);
}

message HelloWorldRequest {
    string name = 1;
}

message HelloWorldResponse {
    string message = 1;
}

To generate the Go code for this service, run the following command. This command expects your proto file in a directory called pb in your project.

protoc -I pb --go_out=pb --go_opt=paths=source_relative --go-nats_out=pb --go-nats_opt=paths=source_relative pb/hello_world.proto

This obviously requires the protoc compiler to be installed on your system and also having the go protobuf plugin installed, so that besides the code regarding NATS can be generated, the messages and everything else can also be generated.

Now you can use the generated code to create a NATS server and client:

package main
import (
    "fmt"
    "github.com/nats-io/nats.go"
    "github.com/user/repo/pb"
)

type serviceImpl struct {}

func (s *serviceImpl) HelloWorld(req *pb.HelloWorldRequest) (*pb.HelloWorldResponse, error) {
	msg := fmt.Sprintf("Hello, %s!", req.GetName())
	return &pb.HelloWorldResponse{Message: msg}, nil
}

func main() {
	nc, _ := nats.Connect(nats.DefaultURL)
	pb.NewHelloWorldServiceNATSServer(nc, &serviceImpl{})
}

Client:

package main
import (
	"github.com/nats-io/nats.go"
    "github.com/user/repo/pb"
)

func main() {
	nc, _ := nats.Connect(nats.DefaultURL)
	cli := pb.NewHelloWorldServiceNATSClient(nc)

	// List all instances currently connected
	instances, err := cli.ListInstances()

	// Get stats from all instances
	stats, err := cli.Stats()

	// Or, from a specific instance:
	stats, err := cli.Stats(pb.WithInstanceID(instances[0].ID))

	// Obviously, you can also call your defined service methods:
	response, err := cli.HelloWorld(&pb.HelloWorldRequest{Name: "John Doe"})

	// And again for a specific instance, instead of the default load balanced distribution:
	response, err := cli.HelloWorld(&pb.HelloWorldRequest{Name: "John Doe"}, pb.WithInstanceID(instances[0].ID))
}
Custom Errors

You can also send custom errors to the client, but for that you need to add this package to your project:

go get xiam.li/go-nats

Then, you can use the go_nats.ServerError type to send custom errors to the client:

// In any method of your service implementation, do the following
// Or, if you want to return a custom error:
return nil, go_nats.NewServerErr("400", "Unknown Name")

// Or, you can also wrap an existing error for more detailed information:
return nil, go_nats.WrapServerErr(err, "500", "Failed to query database")

// You can also send custom headers using this method:
serverErr := go_nats.NewServerErr("400", "Unknown Name")
serverErr.AddHeader("err-details", "Username is not in the database")
return nil, serverErr

On the client side they are received as ServiceError (Important: ServiceError, not ServerError).

_, err := cli.HelloWorld(&pb.HelloWorldRequest{Name: "John Doe"})
if err != nil {
    serviceErr, isSrvErr := go_nats.AsServiceError(err)
    if isSrvErr {
        fmt.Printf("Got a service error with code %s: %s\n", serviceErr.Code, serviceErr.Description)
    } else {
        fmt.Println("Other different error, usually networking related or an issue with unmarshalling the response")
    }
}

You can also use go_nats.IsServiceError(err) to check if an error is a ServiceError.

There's also an Details field in the ServiceError struct, but that's only used when the server, instead of returning a proper ServerError, only returns a generic error. In that case, the result from that error's Error() will end up in the Details field.

Streaming

Streaming is not yet supported, but is planned for the future. It'll probably be implemented along with better timeout handling, that will come with keepalive messages and therefore also allow streaming.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMarshallingFailed   = errors.New("Failed to marshal proto message")
	ErrUnmarshallingFailed = errors.New("Failed to unmarshal proto message")
)

Predefined Errors

Functions

func IsServiceError added in v0.1.2

func IsServiceError(err error) bool

Types

type DoneHandler

type DoneHandler interface {
	Done(service micro.Service)
}

DoneHandler is an interface that when implemented on the server, will be used directly instead of having to use the WithDoneHandler option.

type ErrHandler

type ErrHandler interface {
	Err(service micro.Service, natsErr *micro.NATSError)
}

ErrHandler is an interface that when implemented on the server, will be used directly instead of having to use the WithErrorHandler option.

type ServerError

type ServerError struct {
	Code, Description string
	Wrapped           error
	Headers           map[string][]string
}

ServerError is a custom error type that can be used to return a statuscode along with an error description and additional to the client.

func NewServerErr

func NewServerErr(code, description string) ServerError

func WrapServerErr

func WrapServerErr(err error, code, description string) ServerError

func (ServerError) AddHeader

func (n ServerError) AddHeader(header, value string) ServerError

func (ServerError) Cause

func (n ServerError) Cause() error

func (ServerError) Error

func (n ServerError) Error() string

func (ServerError) GetHeaders

func (n ServerError) GetHeaders() micro.Headers

func (ServerError) GetOptHeaders

func (n ServerError) GetOptHeaders() micro.RespondOpt

func (ServerError) GetWrapped

func (n ServerError) GetWrapped() []byte

GetWrapped returns the wrapped error as a byte slice, or nil if there is no wrapped error. It's therefore safe to be used directly in a NATS response, for example, like this: ```request.Error(natsErr.code, natsErr.description, natsErr.GetWrapped())```

func (ServerError) SetHeader

func (n ServerError) SetHeader(header, value string) ServerError

func (ServerError) WithHeaders

func (n ServerError) WithHeaders(headers map[string][]string) error

type ServiceError added in v0.1.2

type ServiceError struct {
	Code, Description, Details string
}

ServiceError is returned when the server returns an error instead of the expected message.

func AsServiceError added in v0.1.2

func AsServiceError(err error) (ServiceError, bool)

func (ServiceError) Error added in v0.1.2

func (e ServiceError) Error() string

func (ServiceError) Is added in v0.1.2

func (e ServiceError) Is(target error) bool

type StatsHandler

type StatsHandler interface {
	Stats(endpoint *micro.Endpoint) any
}

StatsHandler is an interface that when implemented on the server, will be used directly instead of having to use the WithStatsHandler option.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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