architecture

Drew Verlee 2025-08-27T23:08:15.994039Z

I'm always perplexed when i see exceptions thrown in situations where i can't fathom crashing the system e.g https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

async function getData() {
  const url = "";
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Response status: ${response.status}`);
    }

    const result = await response.json();
    console.log(result);
  } catch (error) {
    console.error(error.message);
  }
}
Why are systems built like this, what is the intuition on why a non 200 response should result in throwing an exception?

2025-08-28T00:15:47.213699Z

Doesn't mean it crashes the system. Could be handled higher up

seancorfield 2025-08-28T00:31:21.701739Z

Their example code contains try / catch so the exception does not propagate. Since fetch() can throw exceptions, you're going to need try / catch somewhere, so it can make sense for non-`ok` responses to also be handled as an exception.

🤷‍♂️ 1
seancorfield 2025-08-28T00:33:37.566029Z

I think the default behavior for most Clojure HTTP client libs is to throw on non-200 responses? I always pass the option to not throw, so I can handle non-200 responses myself. Just noting there's plenty of precedent for this.

Drew Verlee 2025-08-28T00:40:01.750479Z

I'm questioning why fetch throws in the first place. (I did misspeak about the throw in this code fwiw).

Drew Verlee 2025-08-28T00:41:34.463699Z

Why define a network timeout as a reason to throw, but not a status 500.

➕ 1
Drew Verlee 2025-08-28T00:44:28.517219Z

I don't need everything to be the same, but i would like some intuition on why this distinction exists.

seancorfield 2025-08-28T00:47:15.865269Z

For a network timeout/failure, you're not going to get a response back -- there's no status code etc -- so it's inherently not the same type of failure as a request that returns a non-200 status (and body). So their choice is to complicate the response object so you can tell whether the request completed or not, and what sort of failure it yields; or throw an exception for those cases, and keep the response object just for completed requests.

👀 1
➕ 1
seancorfield 2025-08-28T00:48:43.422449Z

fetch() follows the principle of using exceptions for "unexpected" errors and assuming you'll handle them somewhere up your call tree (possibly at the top). And "expected" errors are in the response object as status codes etc.

2025-08-28T00:52:44.446609Z

I think a lot of languages that sit in-between functional error styles and more imperative ones tend to be inconsistent on which error is a thrown exception and which is a returned error.

👆 1
2025-08-28T00:53:48.161989Z

Even if they try to follow some form of "criteria" like Sean mentioned

Drew Verlee 2025-08-28T00:54:02.516199Z

Thanks. @seancorfield i guess that is the intuition: it's not a response so it's an expectation. For my part, i feel like i would just wrap that in a try catch and turn it into data and then pass that on, not re-throw it.

Drew Verlee 2025-08-28T00:54:55.484159Z

whats confusing is the notion that responses would be handled a 'different place' then these exceptions.

seancorfield 2025-08-28T00:56:30.047479Z

Could, not would.

2025-08-28T00:57:08.824209Z

Ya, honestly I don't know. Maybe the author of this code tried to think what they consider "expected and unexpected" and separate thrown vs return based on that. But I could also see that, it was more accidental, the promise doesn't have a result and they were like, oh I should throw. Without much more thought.

seancorfield 2025-08-28T00:57:24.109129Z

In the example code, non-200 responses are turned into exceptions and all types of "failure" are then handled by catch. But you don't have to do that.

👍 1
1
2025-08-28T00:58:53.844169Z

Oh nevermind, my JavaScript is too rusty. Thought response.ok was checking if the promise was in error or not.

👍 1
2025-08-28T01:00:27.908839Z

So wait, then this code basically returns only a successful value or throws. Seems consistent then no?

seancorfield 2025-08-28T01:00:29.882479Z

If it didn't await directly on fetch(), it could handle the promise rejecting explicitly in the code (via callbacks). It's the await that throws when a promise rejects, right? (I don't generally do JS but that's what I assume).

2025-08-28T01:01:38.073349Z

Or I guess because of the catch around it all, it returns the success value or null ?

2025-08-28T01:03:05.641189Z

Lol no wait haha. This function returns nothing and never throws 😛. It just logs

seancorfield 2025-08-28T01:03:49.872489Z

I think Drew is more concerned with fetch() itself than the calling code...

2025-08-28T01:04:46.889399Z

Ah ok lol, but that function threw me off a cliff lol. Why it's called getData 😵‍💫

seancorfield 2025-08-28T01:05:24.501299Z

I think at this point we can all agree it's a poor example of supposed user code that invokes fetch() 🙂

2025-08-28T01:07:59.543339Z

I see, so @drewverlee your question is more about this line: > The fetch() function will reject the promise on some errors, but not if the server responds with an error status like 404: so we also check the response status and throw if it is not OK.

2025-08-28T01:09:15.876169Z

I think then Sean is correct. Fetch fails the promise if the fetch failed (unable to receive a response). If the response indicates an "error", it doesn't consider that an error in the fetch, so it leaves that to be handled as a response for the code fetching.

2025-08-28T01:11:09.210979Z

It's the code example that creates confusion, "!response.ok" is not checking that the fetched promise is not in error. So why is the example than doing what it does for status 200 versus other statuses who knows, it's a poor example. (well it appears to do it because it wants to log the json response and I'd imagine that's only there if it's a 200