简体   繁体   中英

Empty object returned from gRPC Server

Basically, the JSON object that's returned from a callback in my gRPC server is empty no matter what I do.

For the most part I'm following this tutorial, except I'm using a SQLite3 server instead of knex, and I've worked to the listProducts method. I haven't tried working on the other product methods yet.


In server.js I get some data from a SQLite3 database, and try to return it in a callback (at the bottom of the method). I also print out the data from the DB to confirm I'm actually getting valid data.

gRPC server.js

function listProducts(call, callback) {
  console.log("******** Listed the products *********");
  var data = "";

  let db = new sqlite3.Database('../data/testDB.db', sqlite3.OPEN_READONLY, (err) => {
    if(err){
    console.error(err.message);
    }
    console.log("connected to DB");
  });

  db.serialize(() => {
    db.get('SELECT NAME as name FROM PEEPS', (err, row) => {
          if(err){
            console.error(err.message);
          }
          console.log(row.name);
          data.name = row.name;
    });
  });

  db.close((err) => {
    if(err) {
      console.error(err.message);
    }
    console.log('closed db');
  });

  callback(null, { products:  data.name });
}

Out put from gRPC server.js

******** Listed the products *********
connected to DB
Jeff            // Correct data from DB. 
closed db

The callback returns to client.js , where it was called. However, the object is always empty.

If I uncomment res.json({ name: "jessie" }); and comment res.json(result); , the code works as expected; name: jessie is sent to the browser as a JSON object.

So that tells me that from the client to the browser the data is being handled correctly. Therefore the problem is when the data is passed from the server.js to client.js .

gRPC client.js

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

// gRPC client
const productProtoPath = path.join(__dirname, '..', '..', 'protos', 'product.proto');
const productProtoDefinition = protoLoader.loadSync(productProtoPath);
const productPackageDefinition = grpc.loadPackageDefinition(productProtoDefinition).product;
const client = new productPackageDefinition.ProductService('localhost:50051', grpc.credentials.createInsecure());

// handlers
const listProducts = (req, res) => {
  client.listProducts({}, (err, result) => {
      console.log(result);
      console.log(typeof result);
      // console.log(res.json(result));
      res.json(result);
      // res.json({ name: "jessie" });
      console.log("*******************");
  });
};

Output from gRPC client.js

Server listing on port 3000
{}                 //Oh no! An empty JSON object!
object
*******************

Edit Here is a link to my repository: https://github.com/burke212/grpc-node

The main problem here is that in your server code, your db methods are asynchronous but you are trying to access the result synchronously. You need to call the main callback for listProducts in the callback for db.get to ensure that you have the result of that database request before trying to use it. After making this change your listProducts method implementation should look more like this:

function listProducts(call, callback) {

  let db = new sqlite3.Database('../data/testDB.db', sqlite3.OPEN_READONLY);

  db.serialize(() => {
    db.get('SELECT NAME as name FROM PEEPS', (err, row) => {
          if(err){
            console.error(err.message);
          }
          // Call the callback here to use the result of db.get
          callback(null, { products:  row.name });
    });
  });

  db.close();

}

For simplicity I omitted the logging. Also, the sqlite3.Database constructor and db.close do not have callbacks in the example in the sqlite3 README. I suggest checking again whether those functions actually take callbacks.

In addition to that, now that you have shared the product.proto file that defines your service, there is another problem. The listProducts method in the ProductService service is declared as returning a ProductList object. In that message type, the products field must be an array of Product objects. All of the code in your method implementation is directed towards returning a string in that field, and that does not result in a compatible object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM