7

I have the following proto:

syntax = "proto3";

import "google/rpc/status.proto";

message Response {   
    google.rpc.Status status = 1;
}

message Request {   
    Type name = 1;
}

service Service {
    rpc SomeMethod (Request) returns (Response);
}

And I am writing a client in node:

    const path = require('path');
    const grpc = require('grpc');
    const protoLoader = require('@grpc/proto-loader');
    const protoFiles = require('google-proto-files');

    const PROTO_PATH = path.join(__dirname, '/proto/myproto.proto');

    const packageDefinition = protoLoader.loadSync(
      PROTO_PATH,
      {
        keepCase: true,
        longs: String,
        enums: String,
        defaults: true,
        oneofs: true,
        includeDirs: [protoFiles('rpc')],
      },
    );

    const proto = grpc.loadPackageDefinition(packageDefinition);
    const client = new proto.Service('localhost:1111', grpc.credentials.createInsecure());

When I run the client, I get the following error: TypeError: proto.Service is not a constructor. I found it's related to the import of status.proto. What is the right way of importing google protos using proto-loader? The server is in Java.

Olga
  • 309
  • 4
  • 16

2 Answers2

9

Olga, you cannot use the absolute path in the PROTO_PATH if you are using includeDirs. Apparently you need to put both path, i.e. path to myproto.proto AND path to the google-proto-files into includeDirs and use just file name as PROTO_PATH then it works just fine. See here:

https://github.com/grpc/grpc-node/issues/470

Here is modified code that works. Please note that I also had to replace "Type" with "int32" in the myproto.proto.

const path = require('path');
const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');
const protoFiles = require('google-proto-files');

const PROTO_PATH = 'myproto.proto';

const packageDefinition = protoLoader.loadSync(
  PROTO_PATH,
  {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
    includeDirs: ['node_modules/google-proto-files', 'proto']
    },
  );

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const client = new protoDescriptor.Service('localhost:1111',  grpc.credentials.createInsecure());

Hope it helps. :)

  • 1
    Works with absolute paths: const IMPORT_PATH = path.join(__dirname, '/proto'); const GOOGLE_PROTOS = path.join(__dirname, '../node_modules/google-proto-files'); and includeDirs: [GOOGLE_PROTOS, IMPORT_PATH] – Olga Oct 16 '18 at 18:53
  • Yes, but that's what I meant. You cannot use absolute path in the first argument, i.e. main proto file to load if you also specify includeDirs parameter. As for the includeDirs elements themselves, you can use either relative path(s) or absolute one(s) as you please. This should be documented in the loadSync() caveats or implementation details section, IMHO. – Maxim Sobolev Oct 16 '18 at 20:18
  • Didn't work with relative paths in insludeDirs, only with absolute ones. – Olga Oct 16 '18 at 21:36
  • I am the maintainer of that package. This constraint is not documented because it is actually a bug in how `includeDirs` is handled. As for relative `includeDirs` paths, I would recommend against using them because the paths may not be relative to what you expect them to be relative to. – murgatroid99 Oct 17 '18 at 20:31
0

The problem here is that the path that protoFiles('rpc') returns doesn't work with the import line in your .proto file. That import line means that @grpc/proto-loader is looking for an include directory that contains google/rpc/status.proto, but protoFiles('rpc') returns a directory that directly contains status.proto. So, you have to change one or both of those things so that the relative directories match up properly.

murgatroid99
  • 19,007
  • 10
  • 60
  • 95
  • Changed to const IMPORT_PATH = path.join(__dirname, '/../node-modules/google-proto-files'); and includeDirs: [IMPORT_PATH], but get the same error - Service is not a constructor. – Olga Oct 15 '18 at 17:53
  • OK, I have been able to confirm that there is some other issue here. I am still fairly confident that that the issue I mentioned is part of the problem. – murgatroid99 Oct 15 '18 at 19:15
  • I know the issue is with the import, because I could successfully create the client without using status.proto (commented it out), but I can't find anywhere how to correctly load google protos. – Olga Oct 15 '18 at 20:00