0

I have been learning about gRPC, and I was trying to implement a gRPC API to create, get and list to-dos. Here is the server side code of the API:

https://github.com/girikgarg8/NodeJS/tree/master/GRPC_Node

And here is the client side code of the API:

https://github.com/girikgarg8/NodeJS/tree/master/Client_NodeGrpc

The server successfully starts on port 50051, but when I try to run the index.js file from the client, it gives me an error:

E:\Backend Development in NodeJS\Client_NodeGrpc\index.js:18
client.listTodos({}, (err, response) => {
       ^

TypeError: client.listTodos is not a function
    at Object.<anonymous> (E:\Backend Development in NodeJS\Client_NodeGrpc\index.js:18:8)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)

Any 
        at Function.Module._load (node:internal/modules/cjs/loader:822:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
        at node:internal/main/run_main_module:17:47

Any leads on how to fix this error are appreciated!

Girik Garg
  • 87
  • 7

1 Answers1

1

You must be precise in your references (e.g. listTodo not listTodos)

It's a good idea to follow the existing examples closely as you "boot" into a new topic.

I encourage you to:

  1. Add package something; to your todo.proto
  2. Conventionally (!) protos should be shared between clients and servers

So...

  1. Move todo.proto to its own directory e.g. protos
.
├── client
├── protos
└── server

In todo.proto:

syntax="proto3"; //indicates we are using proto version 3

// Use a more applicable name
package foo;

service TodoService{ //we can define API contract too, in the protocol buffer
    rpc CreateTodo (Todo) returns (Todo) {}
    rpc GetTodo (TodoRequest) returns (Todo) {}
    rpc ListTodo (Empty) returns (TodoList) {}
}

message Empty {}

message Todo{
    string id=1; //field numbers are used to identify fields in binary encoded data
    string title=2;
    optional string content=3;
}

message TodoList{
    repeated Todo todos=1; //repeated field indicates that there would be an array of ToDos coming in
}

message TodoRequest{
    string id=1; // the REST equivalent of this would be /todos/:id (resource based ) vs action based
}

server/index.js:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDefinition = protoLoader.loadSync('../protos/todo.proto', {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
});

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);

// The suffix is the package name
var todoService = protoDescriptor.foo;

const server = new grpc.Server();

const todos = [{
    id: '1',
    title: 'Todo1',
    content: 'Content of todo 1'
},
{
    id: '2',
    title: 'Todo2',
    content: 'Content of todo 2'
}
];

// todoService = protoDescriptor.foo
// TodoService is the name of the gRPC service
server.addService(todoService.TodoService.service, {
    // below I am defining the RPCs 
    // Your RPC is called `listTodo`
    listTodo: (call, callback) => {
        callback(null, { todos: todos }); // error first callback, as error is null
    },
    createTodo: (call, callback) => {
        let incomingNewTodo = call.request;
        todos.push(incomingNewTodo);
        callback(null, incomingNewTodo);
    },
    getTodo: (call, callback) => {
        let incomingRequest = call.request;
        let todoId = incomingRequest.id;
        const response = todos.filter((todo) => todo.id == todoId);
        if (response.length > 0) {
            callback(null, response);
        } else {
            callback({
                message: 'Todo not found'
            }, null);
        }
    }
});

server.bindAsync('localhost:50051', grpc.ServerCredentials.createInsecure(), () => {
    console.log("Server started")
    server.start();
});

And: client/index.js:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const packageDefinition = protoLoader.loadSync('../protos/todo.proto', {
    keepCase: true,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true
});

const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
const todoService = protoDescriptor.foo;

const client = new todoService.TodoService('localhost:50051', grpc.credentials.createInsecure());

client.listTodo({}, (err, response) => {
    if (err) {
        console.error(err);
        return;
    }
    console.log(response);
});

And when running:

{
  todos: [
    {
      id: '1',
      title: 'Todo1',
      content: 'Content of todo 1',
      _content: 'content'
    },
    {
      id: '2',
      title: 'Todo2',
      content: 'Content of todo 2',
      _content: 'content'
    }
  ]
}
DazWilkin
  • 32,823
  • 5
  • 47
  • 88