Why can't I make express route synchronous

I know what is wrong with my code and I have looked into the best way of solving it, however with my lack of experience, I am having a hard time finding a good answer.

I need my first route(/data) to be fully completed before the second(/logo) express route sends the data. In short, I just need the variable symbolUrl to be completed before it goes into the second fetch call. Here is the code down below to explain

    app.use(express.static('public'));

    const url =
        'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest';

    const qString =
         '?CMC_PRO_API_KEY=' + process.env.apiKey + '&start=1&limit=10&convert=USD';

    let symbol = [];
    
    app.get('/data', async (req, res) => {
      const fetch_res = await fetch(url + qString);
      const coinData = await fetch_res.json();
    
      for (let i = 0; i < 9; i++) {
        symbol.push(coinData.data[i]['symbol']);
      };
      res.json(coinData);
    });
      
    app.get('/logo', async (req, res) => {
      const symbolUrl = symbol.join(',');
      const url2 = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/info';
      const qString2 = `?CMC_PRO_API_KEY=${apiKey}%symbol=${symbolUrl}`;
      const fetch_res2 = await fetch(url2 + qString2);
      const coinLogo = await fetch_res2.json();
      res.json(coinLogo);
    });

The issue I am trying to solve with this project is that I want to send the data(/data) to be sent to the front end first because this API call will load the majority of the page. Then my second call will load images and other larger files afterward. HOWEVER, the API I am working with to get the logos(images) of the specific crypto coins I want, I need a different endpoint as well as use %symbol=${symbolUrl} in the API call to get the correct tokens I want to call.

client code:

fetch('http://localhost:2000/data')
  .then(async (response) => {
    return response.json();
  })
  .then(async (data) => {
    const parsedData = data['data'];
    // console.log(data['data'][0]['name'])

    await parsedData.forEach((element) => {
  // this just has about 20 lines of code generating the the look of the page. It works as intended 
});

 fetch('http://localhost:2000/logo')
    .then(async (response) => {
      return response.json();
    })
    .then(async (logo) => {
      console.log(logo)});
      

***I have tried putting this in an async function and awaiting the first fetch call

All I need to be done is for app.get(/data) to be fully complete before doing my second app.get. I have done testing and I know that is the issue. I apologize if it is something easy, but I couldn't find anything on making an app.get synchronous and I have tried putting both in a async function, however that did not work.


Solution 1:

You cannot send responses in fragments like you're trying to do, it would throw an error saying Can't set headers after they are sent to client

The proper method to implement what you are trying to do is to define the first layer as middleware, and then allow the second layer to return the response. Here layer basically means a function handler.

In order to control when the execution passes to the next layer / next function handler, express has a third parameter (request, response, next). You're only using request and response, researching about next will solve your concern. Express next function, what is it really for?

First handler

app.get('something_unique', async (req, res, next) =>  {
  // do whatever you want to do first
  // save data into res.locals
  res.locals.foo = {...}
  next()
})

Second Handler

app.get('something_unique', (req, res) => {
    const data = res.locals.foo;
    // whatever you want
    return res.json({ anything })
})

More:

  1. Express next function, what is it really for?
  2. Error: Can't set headers after they are sent to the client
  3. Passing variables to the next middleware using next() in Express.js