DEV Community

Error Handling for fetch in TypeScript

Jesse Warden on February 18, 2025

Error Handling for fetch in TypeScript The following post describes why and how you do error handling for fetch. Why Care? W...
Collapse
 
webjose profile image
José Pablo Ramírez Vargas • Edited

Also, you could use wj-config's ability to create URL-building functions from configuration to ensure your URL's are never malformed.

Assuming a configuration like this:

{
  ...,
  urls: {
    rootPath: '/api',
    users: {
      rootPath: '/users',
      byId: '/{userId}',
      all: '',
    },
    // ETC.  Other sections.
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

You could create a configuration object like this:

import wjConfig from 'wj-config';
import myConfig from './config.json';

export default await wjConfig()
  .addObject(myConfig)
  .createUrlFunctions('urls')
  .build();

Enter fullscreen mode Exit fullscreen mode

Then import it wherever needed:

import config from './config.js';

const userUrl = config.urls.users.byId({ userId: 123 });
// Now use userUrl with full confidence that that URL is not malformed.
// Doing this saves you from having to check in runtime the URL's validity.
const response = await fetcher
  .for<200, MyData>()
  .for<400, Error[]>()
  .fetch(userUrl, options);
...
Enter fullscreen mode Exit fullscreen mode

Full documentation: URL Building Functions

Collapse
 
jesterxl profile image
Jesse Warden

Weird, that library is a JavaScript library, not a TypeScript library. The point wasn't so much "The URL can be bad", but rather, new URL is not safe to utilize, ensure you wrap it with a Result type.

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

What do you mean "not a TypeScript" library? Also, which one? wj-config or dr-fetch?

Thread Thread
 
webjose profile image
José Pablo Ramírez Vargas

Also, you don't need to worry about URL's so long you control it. Again, actual validations are only needed on foreign data. Data you control, like your application's own URL's are expected to be always perfect. If not, the developer will fix before deploying.

Thread Thread
 
jesterxl profile image
Jesse Warden

TypeScript does not yell at you for going new URL('cow'), compiles, and your code then explodes at runtime. We most certainly need to worry.

Thread Thread
 
jesterxl profile image
Jesse Warden

This article is about utilizing types to ensure your code is safe for handling errors from fetch. It doesn't appear wj-config has compile time safety for creating URL's. It looks like you can still create bad ones, and you won't find out until you run the code, as runtime exceptions vs. compile time safety with TypeScript.

Thread Thread
 
webjose profile image
José Pablo Ramírez Vargas

The developer would catch this by testing.

Unless the URL comes from user input or a similarly untrusted source, you don't need to validate the URL's you, as developer, hardcode or sets in config files.

Thread Thread
 
webjose profile image
José Pablo Ramírez Vargas

And this is a problem because...? Again, configuration files are created by developers and are a trusted source of data. Trusted sources don't require validation.

Let me know if I should explain this differently.

Thread Thread
 
jesterxl profile image
Jesse Warden

An example would be YAML. Many think infrastructure as code should be actual code; like TypeScript or PKL. This is because we can write tests for that code, and use language features like types. YAML has none of that and leads to a lot of failed deployments.

Configuration, created by developers, isn't trustable; we need types and/or tests to validate it before we actually run it by shifting left; finding out sooner, failing faster.

Thread Thread
 
webjose profile image
José Pablo Ramírez Vargas

Hello. It seems that you may have deleted one message. I see 2 of my messages in sequence. I don't remember what that message said, so replying to this one is more difficult.

We are talking here about developer-created configuration. You say it is not trusted. You seem to be confused about the definition of "trusted". If you cannot trust yourself, who will you trust? Are you thinkink you might sabotage yourself? This is so weird.

Write types, yes, write tests, yes, validate! All that, Yes. But do it during the development cycle. Once development is done, don't add runtime checks because the configuration has been thoroughly tested.

Pay the price during development, don't repeatedly pay it during runtime.

Collapse
 
webjose profile image
José Pablo Ramírez Vargas • Edited

You could also type the response depending on the value of the status code using dr-fetch:

import { DrFetch } from 'dr-fetch';

const fetcher = new DrFetch();

const response = await fetcher
  .for<200, MyData>()
  .for<400, Error[]>()
  .fetch(url, options);

// Now response is fully typed.  Just write IF statements based on status code or ok:
if (response.ok) { // Also works:  response.status === 200
  response.body; // This is of type MyData.
else {
  response.body; // This is of type Error[].
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jesterxl profile image
Jesse Warden • Edited

Do you know how thorough dr-fetch is for data validation? Angular does the same thing with this.http.post<MyData> but does NOT actually validate the JSON coming back matches MyData, hence why we have to use Zod under the covers, so has me curious.

Collapse
 
webjose profile image
José Pablo Ramírez Vargas

If you or your team are the author of the API, you don't need to waste cycles actually testing the veracity of the TS types. You only need actual validation on data you don't control, which is the lesser cases.

Even when consuming 3rd party API's like a currency API, it is almost a guarantee that actual type checking is wasted effort.

Basically, as a rule of thumb, actual validation should be in place in just few cases, such as reading a value from session/local storage, validating user input, uploaded data files. That sort of thing. API's? Rarely if ever.

Thread Thread
 
jesterxl profile image
Jesse Warden

We certainly do need to test our API's. There is no guarantee just because we wrote it, the code is bug free. We should utilize both types and automated unit and accepteance tests to validate it. Using types helps in a variety of ways to both ensure the API and UI are on the same page with the types, the domain objects, the contract we're expecting to use even if we are also the consumers, and automated tests should be run as contract tests to ensure these assumptions actually code. We could be in a situation where our API is deployed independently of the UI, and this could break if the UI is deployed without updating the types. Types can significantly help, quickly identify these areas, ensure we've handled situations where they break, and also reduce how much testing we have to do, instead relying on the compiler to find these issues.

Thread Thread
 
webjose profile image
José Pablo Ramírez Vargas

Yes, unit-test, debug, refine, all that. Just don't add overhead to consumers of the API. It is expected that the API has gone (past tense) through this process. Once you have ensured its quality, why would you waste CPU cycles checking every single entity you pull from the (already-tested-and-debugged) API?? It makes zero sense.