Alejandro
Alejandro

Reputation: 637

Callback chaining in nodejs Google API

I have two files, app.js and sheets.js. I would like to pass data from app.js into sheets.js, which calls the Google Sheets API, then returns a value once it finishes the api call in the readData() function.

The problem is the following: When I attempt to to return the value of response in app.js as Sheets.readData(data) it returns as undefined. I believe something in the callback chain is not allowing the returned value from readData() to be passed into readSheet() and therefore returning as undefined. Am I missing a callback in readSheet that can return the authorize() result? Or am I missing a callback function somewhere else? Thank you.

// Load client secrets from a local file.
exports.readSheet = function(data, callback) {
  let response = fs.readFile('client_secret.json', function processClientSecrets(err, content) {
    if (err) {
      console.log('Error loading client secret file: ' + err);
      return;
    }
    // Authorize a client with the loaded credentials, then call the
    // Google Sheets API.
    authorize(JSON.parse(content), data), readData);
  });

 return response;
}

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 *
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, data, callback) {
  var clientSecret = credentials.installed.client_secret;
  var clientId = credentials.installed.client_id;
  var redirectUrl = credentials.installed.redirect_uris[0];
  var auth = new googleAuth();
  var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, function(err, token) {
    if (err) {
      getNewToken(oauth2Client, callback);
    } else {
      oauth2Client.credentials = JSON.parse(token);
      callback(oauth2Client, data);
    }
  });
}

function readData(auth, data) {
  var sheets = google.sheets('v4');
  sheets.spreadsheets.values.get({
    auth: auth,
    spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
    range: 'Class Data!A2:E',
  }, function(err, response) {
    if (err) {
      console.log('The API returned an error: ' + err);
      return;
    }
    var rows = response.values;
    if (rows.length == 0) {
      console.log('No data found.');
    } else {
      /* Return value here
      let data = data + 'hello world';
      return data
      */
    }
  });
}

Upvotes: 0

Views: 212

Answers (1)

Explosion Pills
Explosion Pills

Reputation: 191789

readData runs asynchronously and depends on the completion of sheets.spreadsheets.values.get which is asynchronous. There are several ways to handle this. The simplest in your case would be to pass a callback function to readData:

function readData(auth, data, cb) {
  /* your code here */
    } else {
      // successful response from sheets
      cb(null, response);
    }
  });
}

Then you would have to update your call to authorize to call callback instead of just readData:

authorize(JSON.parse(content), data, (auth, data) => readData(auth, data, callback);

However from what I can tell, the Google APIs support promises. It would be much easier / cleaner to write the code to use promises. You can use the mz library for fs methods that have promise support.

// NOTE! This is a simple example with no error handling

exports.readSheet = async function (data) {
  const content = fs.readFile('client_secret.json');
  const [auth, data] = authorize(JSON.parse(content), data);

  return readData(auth, data);
}

async function authorize(credentials, data, callback) {
  var clientSecret = credentials.installed.client_secret;
  var clientId = credentials.installed.client_id;
  var redirectUrl = credentials.installed.redirect_uris[0];
  var auth = new googleAuth();
  var oauth2Client = new auth.OAuth2(clientId, clientSecret, redirectUrl);

  const token = await fs.readFile(TOKEN_PATH);
  oauth2Client.credentials = JSON.parse(token);
  return [oauth2Client, data];
}

async function readData(auth, data) {
  const sheets = google.sheets('v4');
  return sheets.spreadsheets.values.get({
    auth: auth,
    spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
    range: 'Class Data!A2:E',
  });
}

Upvotes: 0

Related Questions