How to Streamline Airtable Database Queries for Better Performance

I’m working with an Airtable setup that has two connected tables - Courses and Learners. When someone looks up a specific learner, I need to find their courses and then get all other learners in those same courses.

My current approach works but feels really messy. I’m using multiple loops and setTimeout functions to handle the async calls. Here’s what I’m trying to accomplish:

  1. Search for a learner by name
  2. Get their enrolled courses (need actual course names, not IDs)
  3. For each course, find all other enrolled learners
  4. Return everything together

Here’s my current implementation:

app.get('/learner/:name', async (req, res) => {
  try {
    const apiUrl = 'https://api.airtable.com/v0/appXYZ123/learners';
    const searchFilter = `?filterByFormula=SEARCH("${req.params.name}", {FullName})`;
    
    const response = await fetch(`${apiUrl}${searchFilter}`, {
      headers: { Authorization: `Bearer ${process.env.API_KEY}` }
    });
    
    const result = await response.json();
    const learnerRecord = result.records[0];
    const enrolledCourses = learnerRecord.fields['Courses'];
    
    let courseNames = [];
    let allLearnerIds = [];
    let classmates = [];
    
    // Get course names from IDs
    for (let course of enrolledCourses) {
      airtableBase('Courses').find(course, (error, courseRecord) => {
        if (error) throw error;
        courseNames.push(courseRecord.fields.CourseName);
      });
    }
    
    // Get all learner IDs for each course
    for (let course of enrolledCourses) {
      airtableBase('Courses').find(course, (error, courseRecord) => {
        if (error) throw error;
        allLearnerIds.push(courseRecord.fields['EnrolledLearners']);
      });
    }
    
    // Convert learner IDs to actual names
    setTimeout(() => {
      for (let i = 0; i < allLearnerIds.length; i++) {
        let namesInCourse = [];
        for (let learnerId of allLearnerIds[i]) {
          airtableBase('Learners').find(learnerId, (error, learnerRecord) => {
            if (error) throw error;
            namesInCourse.push(learnerRecord.fields.FullName);
          });
        }
        classmates.push(namesInCourse);
      }
    }, 800);
    
    setTimeout(() => {
      const finalResult = courseNames.map((courseName, index) => {
        return {
          course: courseName,
          peers: classmates[index]
        };
      });
      
      res.json({ data: finalResult, searchedLearner: req.params.name });
    }, 1200);
    
  } catch (error) {
    res.json({ error: 'Learner not found in database' });
  }
});

Is there a cleaner way to handle these nested queries? The setTimeout approach feels really hacky. Any suggestions for making this more efficient would be great!

The problem is you’re mixing callback-based operations with async/await, which screws up timing and forces those setTimeout hacks. I’ve dealt with this exact issue working with legacy APIs. Promisify the Airtable operations or use Promise.all() for multiple async calls. For nested queries, batch the requests instead of individual calls per record: javascript const coursePromises = enrolledCourses.map(courseId => new Promise((resolve, reject) => { airtableBase('Courses').find(courseId, (error, record) => { if (error) reject(error); else resolve(record); }); })); const courseRecords = await Promise.all(coursePromises); This cuts out the timing guesswork and makes your code way more predictable. If you’re hitting rate limits, check out Airtable’s batch API instead of firing off individual requests.