In general error handling is in my opinion one of the hardest parts of software engineering. :) Silly comments aside, in my limited experience, gRPC seems to encourage "just" returning a gRPC status code and a string. The examples I like to look at are the Google Cloud APIs. For example, the Spanner ExecuteSql says "if [...] the query fails with a FAILED_PRECONDITION error. Queries [...] might return ABORTED. If this occurs, the application should [...]"
So if you can map your application errors to gRPC errors and get what you want, that seems like the "recommended" way. The other thing I have done in some cases is to redefine "success". For example, maybe the "Login" API should return an object with either the logged in credentials, or a detailed error if they can't be logged in for some reason? Spanner ExecuteSql: https://cloud.google.com/spanner/docs/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.ExecuteSql Google Cloud API Design guide on errors: https://cloud.google.com/apis/design/errors On Tuesday, September 12, 2017 at 9:29:26 PM UTC-4, Francis Chuang wrote: > > I am working on an app built as a set of microservices. Each microservice > will be built using GRPC using Go. In front of these services, there will > be an API gateway (built using GraphQL). > > As an example, there is an authentication microservice that authenticates > users using their email address and password. There is a SignIn GRPC > service method that takes the email address, password and session id of the > user. If authentication is successful, we attach the user's id to their > session (using the session id). > > The problem is that if the authentication fails, we want to surface this > to the UI. In the GRPC method implementation, I can return GRPC status > codes, for example: return nil, status.Error(codes.InvalidArgument, "could > not find user by email address"). In order to work out specifically what is > wrong, the client calling this method would need to parse the error message > (which is not a great idea). > > I've thought about attaching extra details to the error. For example, > using bad request: > https://godoc.org/google.golang.org/genproto/googleapis/rpc/errdetails#BadRequest > > However, the method is littered with multiple copies of this in of each > RPC method: > > s, err := status.Newf(codes.InvalidArgument, "could not find user by email > address").WithDetails(&errdetails.BadRequest{ > FieldViolations: []*errdetails.BadRequest_FieldViolation{ > { > Description: "EMAIL_ADDRESS_NOT_FOUND", > Field: []string{"SignInRequest.email"}, > }, > }, > }) > > if err != nil { > return nil, status.Errorf(codes.Internal, "error marshaling status error: > %s", err) > } > > return nil, s.Err() > > In addition, I also need to keep a list of error codes > (EMAIL_ADDRESS_NOT_FOUND) in the above example and make sure the clients > calling this method checks against them. The API gateway would then look at > the error code and surface the appropriate localized error messages to the > UI. > > The approaches I have tried do not appear to be very nice and have made > working on these RPC methods to be a chore and quite unpleasant. > > Has anyone else successfully solved this problem? If so, what were your > approaches? > > Cheers, > Francis > -- You received this message because you are subscribed to the Google Groups "grpc.io" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/grpc-io. To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/3eeb7ae2-595c-4640-a271-4e8af3e14310%40googlegroups.com. For more options, visit https://groups.google.com/d/optout.
