Saltar al contenido principal

Generación de un servicio gRPC

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Generar estructuras Protobuf a partir de nuestro ent.Schema puede ser útil, pero lo que realmente nos interesa es obtener un servidor real que pueda crear, leer, actualizar y eliminar entidades de una base de datos real. Para lograrlo, ¡solo necesitamos actualizar una línea de código! Cuando anotamos un esquema con entproto.Service, le indicamos a la generación de código de entproto que queremos generar una definición de servicio gRPC. Luego, protoc-gen-entgrpc leerá nuestra definición y generará una implementación del servicio. Edita ent/schema/user.go y modifica las Annotations del esquema:

ent/schema/user.go
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
entproto.Service(), // <-- add this
}
}

Ahora vuelve a ejecutar la generación de código:

go generate ./...

Observa algunos cambios interesantes en ent/proto/entpb:

ent/proto/entpb
├── entpb.pb.go
├── entpb.proto
├── entpb_grpc.pb.go
├── entpb_user_service.go
└── generate.go

Primero, entproto añadió una definición de servicio a entpb.proto:

ent/proto/entpb/entpb.proto
service UserService {
rpc Create ( CreateUserRequest ) returns ( User );

rpc Get ( GetUserRequest ) returns ( User );

rpc Update ( UpdateUserRequest ) returns ( User );

rpc Delete ( DeleteUserRequest ) returns ( google.protobuf.Empty );

rpc List ( ListUserRequest ) returns ( ListUserResponse );

rpc BatchCreate ( BatchCreateUsersRequest ) returns ( BatchCreateUsersResponse );
}

Además, se crearon dos archivos nuevos. El primero, entpb_grpc.pb.go, contiene el stub del cliente gRPC y la definición de la interfaz. Si abres el archivo, encontrarás en él (entre muchas otras cosas):

ent/proto/entpb/entpb_grpc.pb.go
// UserServiceClient is the client API for UserService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please
// refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type UserServiceClient interface {
Create(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*User, error)
Get(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*User, error)
Update(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error)
Delete(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
List(ctx context.Context, in *ListUserRequest, opts ...grpc.CallOption) (*ListUserResponse, error)
BatchCreate(ctx context.Context, in *BatchCreateUsersRequest, opts ...grpc.CallOption) (*BatchCreateUsersResponse, error)
}

El segundo archivo, entpub_user_service.go, contiene una implementación generada para esta interfaz. Por ejemplo, una implementación para el método Get:

ent/proto/entpb/entpb_user_service.go
// Get implements UserServiceServer.Get
func (svc *UserService) Get(ctx context.Context, req *GetUserRequest) (*User, error) {
var (
err error
get *ent.User
)
id := int(req.GetId())
switch req.GetView() {
case GetUserRequest_VIEW_UNSPECIFIED, GetUserRequest_BASIC:
get, err = svc.client.User.Get(ctx, id)
case GetUserRequest_WITH_EDGE_IDS:
get, err = svc.client.User.Query().
Where(user.ID(id)).
Only(ctx)
default:
return nil, status.Error(codes.InvalidArgument, "invalid argument: unknown view")
}
switch {
case err == nil:
return toProtoUser(get)
case ent.IsNotFound(err):
return nil, status.Errorf(codes.NotFound, "not found: %s", err)
default:
return nil, status.Errorf(codes.Internal, "internal error: %s", err)
}
}

¡Nada mal! A continuación, creemos un servidor gRPC que pueda atender las solicitudes de nuestro servicio.