Унарный вызов - один запрос и один ответ. Это основа для всех остальных режимов.

Сервер

type orderServer struct {
    ordersv1.UnimplementedOrderServiceServer
    store Store
}

func (s *orderServer) GetOrder(ctx context.Context, req *ordersv1.GetOrderRequest) (*ordersv1.Order, error) {
    order, err := s.store.Get(ctx, req.GetId())
    if err != nil {
        return nil, err
    }
    return &ordersv1.Order{Id: order.ID, Status: order.Status}, nil
}

Сервер регистрируется на отдельном net.Listener через grpc.NewServer(). Метод принимает контекст и возвращает сообщение или ошибку.

Клиент

conn, err := grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials())
if err != nil {
    return err
}
defer conn.Close()

ctx, cancel := context.WithTimeout(ctx, 500*time.Millisecond)
defer cancel()
order, err := client.GetOrder(ctx, &ordersv1.GetOrderRequest{Id: id})

NewClient не означает успешный вызов: соединение создаётся при обращении. Дедлайн всегда ограничен, а контекст пробрасывается до репозитория и внешних вызовов.

Для production используют TLS вместо insecure.

Ошибки

Ошибку хранилища преобразуют в код gRPC, а не возвращают как обычный fmt.Errorf без смысла. Клиент читает её через status.Code(err) и status.Convert(err).Details().

Регистрация и путь вызова

Серверная реализация сама по себе ничего не слушает. Её нужно зарегистрировать в gRPC-сервере и только потом запускать listener:

lis, err := net.Listen("tcp", ":9091")
if err != nil {
    return err
}
server := grpc.NewServer(grpc.UnaryInterceptor(authInterceptor))
ordersv1.RegisterOrderServiceServer(server, &orderServer{store: store})
return server.Serve(lis)
клиент interceptor метод и store запрос + context после auth один ответ или код ошибки

При старте проверяют и регистрацию, и реальный вызов через порт: забытый Register...Server иначе обнаруживается только как Unimplemented у клиента.

Коротко

  • Сервер реализует метод интерфейса, клиент вызывает его как обычный метод.
  • Дедлайн обязателен, контекст не теряется.
  • TLS настраивается отдельно, insecure только для локального стенда.
  • Ошибки преобразуются в gRPC-коды явно.