Airtable API returns unresolved Promise status

I’m building an app with Node.js and connecting to Airtable’s API. My setup includes two tables - one for coaches and another for positions. I’m trying to fetch coach data and match it with position details using a lookup function.

The problem is that my async function keeps returning Promise pending instead of the actual data. I need to pass this data to my template but it’s not working properly.

router.get('/coaches', function(req, res, next) {
  const pageTitle = 'Coach Directory'
  
  const fetchData = async () => {
    const coachList = await coaches
      .select({maxRecords: 8, view: "Main View"})
      .eachPage(function processPage(coachList, getNext) {
        coachList.forEach(function(coach) {
          positions.find(coach.fields["Current Role"], function(error, position) {
            if (error) { console.error("Lookup failed", error); return; }
            coach.fields.position = position.fields.Title;
            return coach
          });
        });
      });
      getNext();
    }
    
    fetchData()
      .then(function(coachList) {
        console.log(coachList)
        res.render('coaches', {title: pageTitle, coachList});
      });
});

How can I properly handle the async operations to get the resolved data instead of pending promises?

Your fetchData function isn’t returning anything. The eachPage method processes records but doesn’t resolve to the data you need. I hit the same issue with Airtable’s API last year. What fixed it for me was collecting records in an array during the eachPage callback, then resolving that array. You’ll also need to handle the position lookup differently since it’s callback-based - either promisify it or use Promise.all to wait for all lookups before rendering. Right now you’re processing records but never returning them to your template, so you get undefined instead of your coach data.

yep, you’re mixing async/await with callbacks. eachPage expects a callback, so you can’t use await directly. Consider using Airtable’s .all() method instead; it returns a promise and makes handling data simpler.

Your fetchData function isn’t returning anything, and you’re mixing promises with callbacks inside eachPage.

I hit this exact problem with Airtable’s API a few months ago. The eachPage method uses callbacks, but you’re trying to await it - doesn’t work well together.

Here’s what fixed it for me:

router.get('/coaches', async function(req, res, next) {
  const pageTitle = 'Coach Directory'
  
  try {
    const records = await coaches.select({
      maxRecords: 8, 
      view: "Main View"
    }).all();
    
    const coachList = await Promise.all(
      records.map(async (coach) => {
        return new Promise((resolve, reject) => {
          positions.find(coach.fields["Current Role"], function(error, position) {
            if (error) {
              reject(error);
              return;
            }
            coach.fields.position = position.fields.Title;
            resolve(coach);
          });
        });
      })
    );
    
    res.render('coaches', {title: pageTitle, coachList});
  } catch (error) {
    console.error('Error fetching coaches:', error);
    next(error);
  }
});

Main changes: use .all() instead of eachPage, wrap the position lookup in a Promise, and use Promise.all to handle all async lookups at once. Way cleaner than fighting callbacks inside eachPage.