Lesson 21 / الدرس 21

fetch: talking to a server / fetch: مخاطبة خادم

Asking another machine for data, and the two awaits it always takes. Also the one thing about fetch that everybody gets wrong for the first year: a 404 is not an error.

سؤال آلة أخرى عن بيانات، والانتظاران اللذان يتطلبهما دائمًا. وكذلك الشيء الوحيد الذي يخطئ فيه الجميع في fetch طوال السنة الأولى: أن 404 ليست خطأً.

Two awaits, always

const res = await fetch(url);      1. the headers have arrived
const data = await res.json();     2. the body has arrived and parsed

res.ok        true for 200-299
res.status    404, 500, 201 ...

fetch REJECTS only when the request never happened
  - no network, no such host, blocked by CORS
a 404 or a 500 is a successful request with a sad answer:
  if (!res.ok) throw new Error('Server said ' + res.status);
The demonstration stands in a fake server so it runs the same for everyone with no network — the objects are real Response objects and the code around them is exactly what you write against a real one. Press the 404 button first; it is the whole lesson.

Sending, not only asking

const res = await fetch('/api/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ item: 'tea', qty: 2 })
});

// cancelling, and a timeout
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
await fetch(url, { signal: controller.signal });   throws AbortError
Three things are easy to forget here. The body must be a string, which is why JSON.stringify is in the middle of it. The Content-Type header is how the server knows to parse it, and without it most servers see an empty body and report a validation error that mentions nothing about headers. And fetch has no timeout at all by default — a request to a server that never answers stays pending for as long as the page is open, which is what AbortController is for.
SymptomUsually
TypeError: Failed to fetchNo network, wrong host, or CORS
Unexpected token < in JSONThe body was an HTML error page, not JSON
Data is undefinedA missing await, or the shape is not what you assumed
Works in the console, not in the pageDifferent origin — the console runs on the page you are viewing
405 or 404 on a POSTThe method or the path, not the body

The second row is the most common message in this entire course, and it almost never means what it says. Print await res.text() instead of res.json() when you meet it: seeing the actual body answers the question in one step.

Try it live / جرّب بنفسك

Preview / المعاينة

Check yourself / اختبر نفسك

1. Does fetch reject when the server answers 404?

2. What does "Unexpected token < in JSON" usually mean?

3. How do you fix a CORS error?

Your task / مهمتك

On your own page, fetch from a real public API and render three fields from the result. Then deliberately produce three failures and record what each one printed: ask for a path that does not exist, turn your network off mid-request, and point the request at a URL that returns HTML. Write the careful version — checking res.ok and throwing with the status — and show that all three now produce a clear message. Add a five-second timeout with AbortController.

على صفحتك أنت، اجلب من واجهة عامة حقيقية واعرض ثلاثة حقول من النتيجة. ثم أنتج ثلاثة إخفاقات عمدًا وسجّل ما طبعه كل واحد: اطلب مسارًا غير موجود، وأطفئ شبكتك أثناء الطلب، ووجّه الطلب إلى عنوان يعيد HTML. واكتب النسخة الحذرة — تفحص res.ok وترمي بالحالة — وأظهر أن الثلاثة تنتج الآن رسالة واضحة. وأضف مهلة خمس ثوانٍ بـAbortController.

  • A real API request rendering three fields طلب واجهة حقيقي يعرض ثلاثة حقول
  • Three deliberate failures, messages recorded ثلاثة إخفاقات متعمدة برسائلها مسجَّلة
  • res.ok checked, with the status in the thrown message res.ok مفحوصة والحالة في الرسالة المرمية
  • A five-second timeout that actually aborts مهلة خمس ثوانٍ تُجهض فعلًا
How do you want to submit? / كيف تريد التسليم؟