Home > Software engineering >  Why can't I make express route synchronous
Why can't I make express route synchronous

Time:01-21

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.

CodePudding user response:

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

CodePudding user response:

I'm not sure what client code you're really running as it sounds like you've bee trying several things, but this should work to sequence the /data request and the /logo request so that the /logo request is not run until the response from the /data request has been received.:

async function run() {
    const r1 = await fetch('http://localhost:2000/data');
    const data = await r1.json();
    const parsedData = data.data;
    parsedData.forEach((element) => {
        // this just has about 20 lines of code generating 
        // the the look of the page. It works as intended
    });
    const r2 = await fetch('http://localhost:2000/logo');
    const logo = await r2.json();
    return logo;
}

run().then(logo => {
    console.log(logo);
}).catch(err => {
    // handle errors here
    console.log(err);
});

If there is any asynchronous code inside the .forEach(), then we will have to see that also to properly sequence that.


As I've said in my comments, stuffing the data from the first request into a server-side variable is probably the wrong design on the server because two separate clients both issuing /data requests will conflict with one another, creating race conditions. But, you haven't explained what this data is really for or why you're stuffing it into a variable on the server for us to suggest an alternate design.

  •  Tags:  
  • Related