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!
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:
package something;
to your todo.proto
protos
should be shared between clients and serversSo...
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'
}
]
}