A service can also be slow instead of broken: overloaded, it answers after many seconds, or never. fetch waits as long as it takes, and so does your customer, while your shop keeps the request open for nothing.
Give the call a timeout: with { signal: AbortSignal.timeout(1000) } as its second argument, fetch gives up after 1000 milliseconds, 1 second. Giving up throws an error, which would crash the route. Catch it with try and catch: the catch block runs only when a line inside try threw an error, and is skipped otherwise. It catches every error, so check that error.name is "TimeoutError", and throw any other error again. Here it is in a weather app:
let response: globalThis.Response;
try {
response = await fetch(WEATHER + "/today/" + name, { signal: AbortSignal.timeout(2000) });
} catch (error) {
if (error.name === "TimeoutError") {
return res.status(504).json({ error: "The weather service took too long" });
}
throw error;
}
A constant made inside try { } exists only there, so response is declared with let above the try. Declared without a value, it needs its type: the type of what fetch returns is named Response, like the Express type you import for res, so globalThis.Response says you mean the built-in one. The status is 504 Gateway Timeout: like 502, the problem is behind your shop, and 504 says exactly what it was. The customer waits one second at most, and gets an answer either way.
The check makes the supplier take 2 seconds for one request with POST /check/supplier-slow, a page that only exists in the tests. Your shop must answer:
GET /products/1/supplier-stock → 200 {"product_id": 1, "supplier_stock": 120}
POST /check/supplier-slow {"times": 1} → 200 {"slow_next": 1}
GET /products/1/supplier-stock → 504 {"error": "The supplier took too long"}
GET /products/1/supplier-stock → 200 {"product_id": 1, "supplier_stock": 120}
POST /check/supplier-down {"times": 1} → 200 {"down_next": 1}
GET /products/3/supplier-stock → 502 {"error": "The supplier is not answering"}
GET /products/6/supplier-stock → 404 {"error": "The supplier doesn't sell this product"}
Your task: in the /products/:productId/supplier-stock route, declare let response: globalThis.Response; and send the request to the supplier inside a try block, with a timeout of 1000 milliseconds. In catch, answer "The supplier took too long" with 504 when error.name is "TimeoutError", and throw any other error again. The 404 and 502 checks stay below, unchanged.
Assignment
Follow the Coding Tutorial to give the supplier call a time limit and answer 504 when it runs out.
Hint
Inside try, assign to response without const. The return goes inside the if, and throw error; below the if, still inside catch.
Free plan: 30 tutor messages. PRO is unlimited.
