19

I'm trying to use node-fetch with nodejs to make api calls to my personal api. I would like to be able to update certain values synchronously within this periodically as things update/change with my database behind the scenes. I know that async and await exist but with all my googling I still don't quite understand them or how they interact with fetch requests.

This is a bit of example code I'm trying to get working but still just logs undefined

const fetch = require('node-fetch');
const url = 'http://example.com';
let logs;

example();
console.log(logs);
async function example(){
    //Do things here
    logs = await retrieveLogs();
    //Do more things here
}

async function retrieveLogs(){
    await fetch(url)
    .then(res => res.json())
    .then(json => {return json})
    .catch(e => console.log(e))
}
4
  • 3
    await example(); Commented Sep 3, 2018 at 8:44
  • 5
    .then(json => {return json}) this line is pointless. Just remove it. Commented Sep 3, 2018 at 8:51
  • Thanks for the help! Both of those things were somewhat true, but not the whole answer as I still needed to return the fetch like Ali pointed out Commented Sep 3, 2018 at 9:07
  • 2
    Note that using async and await does not make your action synchronous. It is just syntaxic sugar to make your code more elegant and display it like if it was synchronous. Actions are still asynchrnous behind the scene. Commented Sep 3, 2018 at 9:12

5 Answers 5

12

I think you need to return retrieveLogs function result like this:

async function retrieveLogs(){
    return await fetch(url)
    .then(res => res.json())
}
Sign up to request clarification or add additional context in comments.

4 Comments

That did it and makes sense as the await is looking for a promise to be resolved. I didn't realize that you could return an await and thought I needed that second .then to actually get the data being returned. I also made a critical error as I think skyboyer was pointing to that I would have to await the original function call (probably with an anonymous function) otherwise that misplaced console.log would fire prior to example() finishing. Thanks!
Actually upon further inspection it seems I don't need that function to be async and I don't need to await the fetch. I assume the await from the first call in the example() handles that?
Fetch function is a native async function and if you don't wanna to use the await syntax, you can get value by calling then after fetching.
Don't mix await and .then() like this. Don't await if you return anyway. Don't omit error handling of fetch responses.
6

As Ali Torki said in comment, fetch() is an async function and cannot be "made" sync no matter what. If you MUST fetch synchronously with HTTP (e.g. because you MUST use it in a property getter which MUSTN'T be async), then you MUST use a different HTTP client, full stop.

1 Comment

Lots of unnecessarily all-caps words here. It's true that the fetch API is fully async but you can make it sync without a third-party library using a synchronous subprocess for extremely rare use cases where you need to.
6
npm install sync-fetch

Synchronous wrapper around the Fetch API. Uses node-fetch under the hood, and for some input-parsing code and test cases too.

https://www.npmjs.com/package/sync-fetch

1 Comment

YAAS, this is crucial for making top level fetches in node. For example if you are trying to drive a playwright test script.
0

Using Immediately Invoked Async Function Expression:

(async () => {
  try {

    const response = await fetch('http://example.com')
    const json = await response.json()

  } catch (error) {
    console.log(error);
  }
})();

1 Comment

This isn't synchronous, nor does it pertain in any way to OP's problem.
0

The correct answer 99% of the time (including OP's case) is to keep your fetch code async throughtout, as shown in this answer (although they don't need to mix await and then). Network functions like fetch are async for a reason--you don't want to block your single server thread for a long-running request. Node.js efficiency depends on keeping sync work to a minimum and shelling out long-running, blocking/CPU-bound tasks to separate processes.

But for rare occasions when you really need a sync fetch (like this case, where Playwright tests don't support async code for test detection), you can use a synchronous subprocess call (tested in Node 20.11):

const {execSync} = require("node:child_process");

const script = `
fetch("https://jsonplaceholder.typicode.com/todos/1")
  .then(res => res.text())
  .then(console.log);`;
const result = execSync(`node -e '${script}'`, {encoding: "utf-8"});
const data = JSON.parse(result);
console.log(data);

Error handling, proper escaping and so forth are left as an exercise.

The sync-fetch package described in this answer works too, but the above approach doesn't entail a third-party dependency.

3 Comments

I don't want to refactor my entire application inside out in order to lay the async wire all the way to the entrypoint either.
@SzczepanHołyszewski Did you mean to post this comment in the other answer thread? Because this answer helps to do exactly that: avoid async entirely, as OP asked, even though it isn't the best solution for their actual use case, which is a very small amount of rewiring. Async is the way of the language--it's a good thing.
For me this answer is very helpful and out of box. I find down-vote as excessive here...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.