Calling APIs, Background Jobs and Real-Time
By Syncfusion®, Inc. · Last updated
Calling other APIs with fetch, checking response.ok, timeouts, retries with backoff, idempotency keys, caching, background jobs with 202 Accepted, fire-and-forget work, Server-Sent Events and WebSockets.
What you will learn
- Call other APIs with fetch and check the answer
- Add timeouts, retries and idempotency keys
- Cache slow answers
- Run slow work as a background job
- Push live updates with Server-Sent Events
Calling APIs, Background Jobs and Real-Time
Real servers rarely work alone. They call other services, do slow work later, and push live updates.
- Calling an API = your server sends its own request to another server with
fetch. - Timeout = give up if the other server takes too long.
- Retry = try again when a failure is temporary.
- Background job = slow work that runs after the response is sent.
- Real-time = the server sends updates without the browser asking again.
Simple lesson: Your server is also a client. Treat other servers as slow and unreliable.
A Real-World Example
Think about the canteen counter at lunch.
- Priya orders a dosa. The counter calls the kitchen to check stock. That is an API call.
- If the kitchen does not answer in 10 seconds, the counter says "try again soon". That is a timeout.
- The counter gives Priya a token number right away. The dosa is made later. That is a background job.
- The screen above the counter shows "Token 42 ready". That is a real-time update.
In this lesson, you will learn:
- How to call other APIs with
fetch. - How to add timeouts, retries and idempotency keys.
- How to cache answers from slow services.
- How to run slow work in the background and answer
202 Accepted. - How to push live updates with Server-Sent Events, and where WebSockets fit.
The labs use pretend services such as api.weather.lab, pay.lab and mail.lab. They behave like real ones, but never leave your browser.
Calling Another API with fetch
Node.js 18 and later has fetch built in. No package needed.
await fetch(url)sends the request and gives a Response.response.okistruefor 2xx status codes.fetchdoes not throw for 404 or 500.await response.json()reads the body as JSON.fetchthrows only when the request could not be sent at all, such as a wrong host name.- Send JSON with
method,headersandbody: JSON.stringify(data).
502 Bad Gatewaymeans "another server I depend on failed". Use it when an outside call breaks.encodeURIComponentkeeps spaces and symbols in the city name safe inside the URL.
Simple lesson: Check response.ok yourself. Catch errors for hosts that cannot be reached.
Timeouts, Retries and Idempotency
Outside services can be slow or busy. Plan for it.
- Timeout: pass
signal: AbortSignal.timeout(1000). After 1 second,fetchthrows aTimeoutError. - Answer 504 Gateway Timeout when the other server was too slow.
- Retry only temporary failures: 503, 429 and network errors. Never retry a 400.
- Wait a little longer between tries. This is called backoff.
- Idempotency key = a unique ID sent with a payment. If you retry, the payment service sees the same key and does not charge twice.
- The second payment returns the same charge ID. Priya is charged once.
- In real apps, packages such as
p-retryhandle retries and backoff for you.
Never retry a payment or an order without an idempotency key. A retry after a timeout can charge the student twice.
Simple lesson: Always set a timeout. Retry only safe or keyed requests.
Caching Answers
Calling a slow service on every request wastes time.
- Cache = keep a copy of an answer for a short time.
- Store the value and the time it expires.
- If the copy is still fresh, return it without calling the service.
- Choose a short life. Weather can be 10 minutes old. A bank balance cannot.
- Real apps share one cache between servers with Redis.
- Look at the
X-Cacheheader: MISS, HIT, then MISS again after the copy expires.
Simple lesson: Cache slow answers for a short, safe time.
Background Jobs
Some work is too slow to finish inside one request: making a report, sending 500 emails, resizing photos.
Animation: the client gets 202 and a job ID at once, a worker does the slow job, then the client checks the status and sees done
Client
Express
Worker
202 job 7
status: done
slow work answer now, check later
Answer at once with a job ID. Do the slow work later. The client checks the status.
- Answer 202 Accepted with a job ID straight away.
- Give a status URL such as
/api/jobs/7. The client checks it until the job isdone. - Fire-and-forget: for small extras like a confirmation email, start the work and do not
awaitit. Always add.catch, or a failure crashes the server. - In production, use a job queue such as BullMQ with Redis. Jobs survive restarts and can run on other machines.
- For jobs on a schedule, such as "clear old orders at midnight", use
node-cronor your host's scheduler.
Simple lesson: Answer fast with 202. Do slow work in the background, and always catch its errors.
Live Updates with Server-Sent Events
How does the canteen screen show "Token 42 ready" without refreshing?
- Polling = the browser asks again every few seconds. Simple, but wasteful.
- Server-Sent Events (SSE) = one long response. The server keeps writing new lines.
- Set
Content-Type: text/event-stream, thenres.write("data: ...\n\n")for each event. - The browser reads it with
new EventSource("/api/tokens/stream"), and reconnects by itself. - Listen for
req.on("close")to stop the timer when the user leaves.
- The lab watches the stream for 1.6 seconds, then disconnects. See
screen disconnectedin the terminal.
WebSockets
- WebSocket = a two-way connection. Both sides can send at any time.
- Use it for chat, games and live editing. SSE is enough when only the server talks.
- Express does not handle WebSockets itself. Add the
wsor Socket.IO package on the same HTTP server.
import { createServer } from "node:http";
import { Server } from "socket.io";
const server = createServer(app);
const io = new Server(server);
io.on("connection", (socket) => {
socket.on("order:placed", (order) => io.emit("kitchen:new-order", order));
});
server.listen(3000);
Easy way to remember: SSE is a radio (server talks, you listen). WebSocket is a phone call (both talk).
Simple lesson: SSE for one-way live updates, WebSockets for two-way talk.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Not checking response.ok | fetch throws on 404 and 500 | It only throws on network errors | Error pages are treated as data |
| No timeout | The other server always answers | Use AbortSignal.timeout | Requests hang and pile up |
| Retrying a payment with no key | A retry is always safe | Send an idempotency key | Students are charged twice |
| Retrying a 400 | All errors are temporary | Retry only 503, 429 and network errors | Wasted calls, same error |
Background work with no .catch | The response is sent, so it is fine | Catch every background error | An unhandled rejection crashes the server |
| Slow work inside the request | Users can wait | Answer 202, run it as a job | Timeouts and an unhappy user |
SSE with no close handler | The timer stops by itself | Clear it on req.on("close") | Memory leak as users come and go |
Check Yourself
Exercises
What to Remember
fetchonly throws on network errors. Always checkresponse.ok.- Set a timeout with
AbortSignal.timeout. Answer 502 or 504 when outside services fail. - Retry only temporary failures, with backoff, and use idempotency keys for payments.
- Cache slow answers for a short, safe time.
- Slow work: answer 202 with a job ID, run it in the background, catch every error.
- SSE pushes one-way live updates. WebSockets (
ws, Socket.IO) are for two-way talk.
Next: Logging and Debugging. You will see what your server is doing and find bugs faster.