Node.js: وحدة HTTP

آخر تحديث: 2026-08-26

كان بوب بحاجة إلى التحقق بسرعة من صحة فكرة واجهة برمجة تطبيقات (API)، لكنه لم يرغب في إنشاء مشروع Express كامل، لذا استخدم وحدة HTTP الأصلية لتشغيل خادم API في 20 سطرًا فقط من التعليمات البرمجية. بدءًا من معالجة أساليب الطلبات وتحليل مسارات عناوين URL وصولاً إلى إرجاع بيانات JSON وتعيين رموز الحالة، وجد بوب أنه بمجرد فهمه للمبادئ الأساسية، أصبح استخدام إطار العمل أسهل بكثير بالفعل.

1. ما ستتعلمه



2. إنشاء أول خادم HTTP خاص بك

http.createServer يقبل دالة استدعاء (callback) يتم تشغيلها في كل مرة يتم فيها استلام طلب. تتلقى دالة الاستدعاء معلمتين: request (كائن الطلب) وresponse (كائن الاستجابة). server.listen يحدد منفذ الاستماع.

▶ مثال: تقليل حجم خادم HTTP

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  res.end('Hello, World!');
});

server.listen(3000, () => {
  console.log('Server running at http://localhost:3000/');
});
▶ جرّب الكود
BASH
node server.js
TEXT 📖 للعرض فقط
Server running at http://localhost:3000/

ما عليك سوى زيارة http://localhost:3000/ في متصفحك لعرض Hello, World!.



3. دورة حياة طلبات واستجابات HTTP

يتبع كل تفاعل عبر بروتوكول HTTP المسار التالي: الطلب → التوجيه → المعالجة → الاستجابة؛ ويُعد فهم دورة الحياة هذه الأساس لبناء خدمات الويب.

100%
flowchart LR
    A["Client"] -->|"Send Request"| B["request Object<br/>method / url / headers"]
    B --> C["Routing Resolution<br/>pathname + searchParams"]
    C --> D{"Request Method?"}
    D -->|GET| E["Read Query Parameters"]
    D -->|POST / PUT| F["Collect the request body"]
    E --> G["Business Processing"]
    F --> G
    G --> H["response Object<br/>statusCode / headers / body"]
    H -->|"Return Response"| A


4. الخصائص الأساسية للكائن request

request يحتوي هذا الكائن على جميع معلومات الطلب التي أرسلها العميل؛ وأهم ثلاث خصائص تُستخدم بشكل شائع هي method وurl وheaders.

الخاصية / الطريقة النوع الوصف مثال على القيمة
req.method سلسلة طريقة الطلب 'GET'، 'POST'
req.url سلسلة مسار الطلب (بما في ذلك سلسلة الاستعلام) '/api/users?id=1'
req.headers كائن كائن رأس الطلب { 'content-type': 'application/json' }
req.httpVersion سلسلة إصدار بروتوكول HTTP '1.1'
req.socket كائن كائن المقبس الأساسي

▶ مثال: معلومات طلب الطباعة

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  console.log(`Method: ${req.method}`);
  console.log(`URL: ${req.url}`);
  console.log(`Content-Type: ${req.headers['content-type'] || 'N/A'}`);
  res.end('Check your terminal for request info.');
});

server.listen(3000);
▶ جرّب الكود

أرسل طلبًا تجريبيًّا باستخدام curl:

BASH
curl -X POST http://localhost:3000/api/data -H "Content-Type: application/json"
TEXT 📖 للعرض فقط
Method: POST
URL: /api/data
Content-Type: application/json


5. الطرق الأساسية للكائن response

response يُستخدم هذا الكائن لإرسال بيانات الاستجابة إلى العميل، بما في ذلك رمز الحالة ورؤوس الاستجابة ونص الاستجابة.

الطريقة / الخاصية الوصف مثال
res.writeHead(statusCode, headers) كتابة رموز الحالة ورؤوس استجابة متعددة في عملية واحدة res.writeHead(200, { 'Content-Type': 'text/plain' })
res.statusCode = n ضبط رمز الحالة بشكل فردي res.statusCode = 404
res.setHeader(name, value) تعيين رأس استجابة واحد res.setHeader('Content-Type', 'application/json')
res.write(data) كتابة بيانات نص الرد (يمكن استدعاؤها عدة مرات) res.write('partial')
res.end(data) إرسال نص الاستجابة وإنهاء الاستجابة res.end('done')
res.writeHead بعد استدعائه، ثم res.end() إرسال البيانات المخزنة مؤقتًا

▶ مثال: إرجاع استجابة بتنسيق JSON

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  const data = { message: 'Success', timestamp: Date.now() };

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(data));
});

server.listen(3000);
▶ جرّب الكود
BASH
curl http://localhost:3000/
TEXT 📖 للعرض فقط
{"message":"Success","timestamp":1719792000000}


6. تحديد مسار عناوين URL

يحتوي req.url على مسار الطلب وسلسلة الاستعلام بالكامل. ويُسهّل استخدام new URL() فصل اسم المسار و«searchParams»، مما يتيح التوجيه استنادًا إلى المسار.

▶ مثال: التوجيه القائم على المسار

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const pathname = url.pathname;

  res.writeHead(200, { 'Content-Type': 'text/plain' });

  if (pathname === '/') {
    res.end('Home Page');
  } else if (pathname === '/about') {
    res.end('About Page');
  } else if (pathname === '/api/status') {
    res.end('OK');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3000);
▶ جرّب الكود

7. طلبات GET ومعلمات الاستعلام

يتم تضمين معلمات طلب GET في سلسلة الاستعلام الخاصة بعنوان URL، ويمكنك استرداد أزواج المفاتيح والقيم مباشرةً باستخدام url.searchParams.

▶ مثال: تحليل معلمات الاستعلام

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method !== 'GET') {
    res.writeHead(405, { 'Content-Type': 'text/plain' });
    res.end('Method Not Allowed');
    return;
  }

  const url = new URL(req.url, `http://${req.headers.host}`);
  const name = url.searchParams.get('name') || 'Guest';
  const page = url.searchParams.get('page') || '1';

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ name, page }));
});

server.listen(3000);
▶ جرّب الكود
BASH
curl "http://localhost:3000/?name=Bob&page=3"
TEXT 📖 للعرض فقط
{"name":"Bob","page":"3"}

ملاحظة: تُرجع الدالة searchParams.get() دائمًا سلسلة نصية، لذا يجب عليك تحويلها يدويًّا إلى رقم أو نوع آخر.



8. طلبات POST وجمع نص الطلب

يتم إرسال بيانات طلبات POST عبر نص الطلب. يُعد الكائن request دفقًا قابلًا للقراءة؛ حيث يتعين عليك الاستماع إلى الحدث data لجمع كتل البيانات، والاستماع إلى الحدث end لمعالجة البيانات الكاملة.

▶ مثال: استرداد نص طلب POST

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.method === 'POST' && req.url === '/api/users') {
    let body = '';

    req.on('data', (chunk) => {
      body += chunk.toString();
    });

    req.on('end', () => {
      try {
        const data = JSON.parse(body);
        res.writeHead(201, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ id: 1, ...data }));
      } catch (e) {
        res.writeHead(400, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Invalid JSON' }));
      }
    });
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});

server.listen(3000);
▶ جرّب الكود
BASH
curl -X POST http://localhost:3000/api/users -H "Content-Type: application/json" -d "{\"name\":\"Bob\",\"age\":30}"
TEXT 📖 للعرض فقط
{"id":1,"name":"Bob","age":30}


9. نوع المحتوى ورؤوس الاستجابة

Content-Type يُعد هذا أحد أهم رؤوس الاستجابة في الاتصالات عبر بروتوكول HTTP، حيث إنه يحدد تنسيق بيانات نص الاستجابة المرسلة إلى العميل. وسيؤدي ضبطه بشكل غير صحيح إلى منع العميل من تحليل البيانات بشكل صحيح.

نوع المحتوى الغرض الوصف
text/plain نص عادي النوع الأساسي للنص، بدون أي تنسيق
text/html صفحة HTML يعرض المتصفح هذه الصفحة كصفحة ويب
application/json بيانات JSON تنسيق الاستجابة الأكثر استخدامًا في واجهات برمجة التطبيقات (API)
application/x-www-form-urlencoded بيانات النموذج التنسيق الافتراضي لإرسال النموذج
multipart/form-data تحميل الملفات إرسال النموذج مع الملفات
application/xml بيانات XML واجهة برمجة تطبيقات SOAP أو واجهة قديمة
text/css ورقة أنماط CSS ورقة أنماط
application/octet-stream دفق ثنائي سيناريوهات تنزيل الملفات

▶ مثال: تأثير أنواع المحتوى المختلفة على نفس البيانات

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);

  if (url.pathname === '/plain') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('<h1>This is plain text</h1>');
  } else if (url.pathname === '/html') {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.end('<h1>This is HTML</h1>');
  } else if (url.pathname === '/json') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ message: 'This is JSON' }));
  }
});

server.listen(3000);
▶ جرّب الكود

عند زيارة /plain، يعرض المتصفح علامة تبويب «الكود المصدري»؛ وعند زيارة /html، يعرض المتصفح العنوان الرئيسي؛ وعند زيارة /json، يعرض المتصفح بيانات JSON.



10. مرجع سريع لرموز حالة HTTP

رمز الحالة هو تمثيل موحد لاستجابة الخادم لطلب ما؛ ويحدد العميل الإجراء التالي الذي سيتخذه بناءً على رمز الحالة.

رمز الحالة الفئة المعنى الحالات الشائعة
200 2xx نجاح OK طلب GET أعاد البيانات بنجاح
201 2xx نجاح تم الإنشاء تم إنشاء المورد بنجاح عبر طلب POST
204 2xx نجاح لا يوجد محتوى تم الحذف بنجاح، لم يتم إرجاع أي محتوى
301 إعادة التوجيه 3xx تم النقل بشكل دائم إعادة التوجيه الدائم إلى عنوان URL الجديد
302 إعادة توجيه 3xx تم العثور عليه إعادة توجيه مؤقتة
304 إعادة توجيه 3xx لم يتم التعديل تم العثور على النسخة المخزنة مؤقتًا؛ لا حاجة لإعادة الإرسال
400 خطأ عميل 4xx طلب غير صحيح تنسيق معلمة الطلب غير صحيح
401 خطأ عميل 4xx غير مصرح به لم تتم المصادقة؛ يلزم تسجيل الدخول
403 خطأ عميل 4xx ممنوع تم المصادقة ولكن لا توجد أذونات
404 خطأ العميل 4xx غير موجود المسار أو المورد غير موجود
405 خطأ عميل 4xx الطريقة غير مسموح بها طريقة الطلب غير مسموح بها
500 خطأ الخادم 5xx خطأ داخلي في الخادم خطأ داخلي في الخادم
502 خطأ خادم 5xx بوابة غير صالحة تلقت البوابة/الوكيل استجابة غير صالحة
503 خطأ خادم 5xx الخدمة غير متاحة الخدمة غير متاحة مؤقتًا

▶ مثال: إرجاع رموز حالة مختلفة بناءً على الشروط

JAVASCRIPT
const http = require('http');

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const id = url.searchParams.get('id');

  if (!id) {
    res.writeHead(400, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Missing id parameter' }));
  } else if (id === '0') {
    res.writeHead(404, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'User not found' }));
  } else {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ id, name: 'Bob' }));
  }
});

server.listen(3000);
▶ جرّب الكود

11. مثال شامل: خادم بسيط لواجهة برمجة التطبيقات REST

اجمع بين جميع المفاهيم التي تمت تغطيتها حتى الآن لإنشاء خادم واجهة برمجة تطبيقات REST يدعم مسارات GET/POST، وردود JSON، وتحليل معلمات الاستعلام. احتفظ بقائمة بالمستخدمين في الذاكرة وادعم ثلاث عمليات: الاستعلام عن جميع المستخدمين، والاستعلام عن مستخدم واحد، وإنشاء مستخدم.

JAVASCRIPT
const http = require('http');

const users = [
  { id: 1, name: 'Bob', email: 'bob@example.com' },
  { id: 2, name: 'Alice', email: 'alice@example.com' },
];
let nextId = 3;

function parseBody(req) {
  return new Promise((resolve, reject) => {
    let body = '';
    req.on('data', (chunk) => { body += chunk.toString(); });
    req.on('end', () => {
      try { resolve(JSON.parse(body)); }
      catch (e) { reject(e); }
    });
    req.on('error', reject);
  });
}

function sendJSON(res, statusCode, data) {
  res.writeHead(statusCode, {
    'Content-Type': 'application/json',
    'X-Powered-By': 'Node.js',
  });
  res.end(JSON.stringify(data));
}

const server = http.createServer(async (req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);
  const pathname = url.pathname;

  // GET /api/users
  if (req.method === 'GET' && pathname === '/api/users') {
    const limit = parseInt(url.searchParams.get('limit')) || 10;
    sendJSON(res, 200, users.slice(0, limit));
    return;
  }

  // GET /api/users/:id
  if (req.method === 'GET' && pathname.startsWith('/api/users/')) {
    const id = parseInt(pathname.split('/').pop());
    const user = users.find((u) => u.id === id);
    if (!user) {
      sendJSON(res, 404, { error: 'User not found' });
    } else {
      sendJSON(res, 200, user);
    }
    return;
  }

  // POST /api/users
  if (req.method === 'POST' && pathname === '/api/users') {
    try {
      const data = await parseBody(req);
      if (!data.name || !data.email) {
        sendJSON(res, 400, { error: 'name and email are required' });
        return;
      }
      const newUser = { id: nextId++, name: data.name, email: data.email };
      users.push(newUser);
      sendJSON(res, 201, newUser);
    } catch (e) {
      sendJSON(res, 400, { error: 'Invalid JSON body' });
    }
    return;
  }

  // 404 fallback
  sendJSON(res, 404, { error: 'Route not found' });
});

server.listen(3000, () => {
  console.log('REST API server running at http://localhost:3000/');
});

اختبار جميع الواجهات:

BASH
# Query All Users
curl http://localhost:3000/api/users

# Query a Single User
curl http://localhost:3000/api/users/1

# Create a New User
curl -X POST http://localhost:3000/api/users -H "Content-Type: application/json" -d "{\"name\":\"Charlie\",\"email\":\"charlie@example.com\"}"

# Accessing a Route That Does Not Exist
curl http://localhost:3000/unknown
TEXT 📖 للعرض فقط
[{"id":1,"name":"Bob","email":"bob@example.com"},{"id":2,"name":"Alice","email":"alice@example.com"}]

{"id":1,"name":"Bob","email":"bob@example.com"}

{"id":3,"name":"Charlie","email":"charlie@example.com"}

{"error":"Route not found"}


❓ أسئلة شائعة

س ما الفرق بين وحدة http و Express؟
ج http هي وحدة مدمجة منخفضة المستوى في Node.js توفر فقط المعالجة الأساسية للطلبات والاستجابات؛ أما Express فهو إطار عمل مبني على وحدة http ويتضمن ميزات متقدمة مثل التوجيه، والبرمجيات الوسيطة، ومحرك القوالب، مما يوفر كفاءة أعلى في التطوير ولكنه يضيف تبعيات إضافية.
س كيف يمكنني استرداد نص طلب POST؟
ج الكائن req هو دفق قابل للقراءة. تحتاج إلى الاستماع إلى الحدث data لجمع كتل المخزن المؤقت وربطها معًا، ثم الاستماع إلى الحدث end للإشارة إلى اكتمال استقبال البيانات، وأخيرًا استخدام JSON.parse() أو Buffer.concat() لمعالجة البيانات الكاملة.
س ما الفرق بين res.end() و res.write()؟
ج res.end() ترسل البيانات وتغلق الاستجابة؛ ويجب استدعاؤها مرة واحدة — ومرة واحدة فقط — لكل طلب؛ res.write() تكتب البيانات فقط دون إغلاق الاستجابة؛ ويمكن استدعاؤها عدة مرات من أجل البث المتواصل أو الإرسال على أجزاء، ولكن res.end() يجب استدعاؤها في النهاية لإغلاق الاستجابة.
س لماذا من الضروري تعيين Content-Type عند إرجاع JSON؟
ج إذا لم يتم تعيين Content-Type، فإن القيمة الافتراضية هي text/plain. ولن تقوم العملاء (المتصفحات، وfetch، وcurl) بتحليل الاستجابة تلقائيًا على أنها JSON، مما قد يؤدي إلى حدوث أخطاء response.json() أو عدم عرض البيانات بشكل صحيح. ويضمن تعيينه إلى application/json أن العملاء يعرفون كيفية تحليل نص الاستجابة.
س كيف يمكنني التعامل مع معلمات الاستعلام في عنوان URL؟
ج استخدم new URL(req.url, 'http://localhost') لإنشاء كائن URL، ثم استخدم url.searchParams.get('key') لاسترداد قيم المعلمات، أو استخدم url.searchParams.entries() للتنقل بين جميع المعلمات.
س ما الغرض من دالة الاستدعاء الثانية لـ server.listen؟
ج إنها دالة استدعاء يتم تشغيلها بعد بدء تشغيل الخادم بنجاح، وغالبًا ما تُستخدم لعرض سجلات بدء التشغيل. إذا لم تقم بتمرير دالة استدعاء، فيمكنك أيضًا الاستماع إلى حدث server.on('listening', callback)، الذي يحقق نفس الغرض.
س لماذا يجب أن تتضمن المعلمة الثانية لـ new URL base؟
ج تحتوي req.url على جزء المسار فقط (على سبيل المثال، /api?id=1) وهي ليست عنوان URL كاملاً. يتطلب new URL() معلمة base لإكمال البروتوكول واسم المضيف؛ وإلا، فسيتم إصدار استثناء TypeError. ولا تؤثر قيمة base على نتائج تحليل pathname وsearchParams.

📖 ملخص


📝 تمارين

  1. أكمل جميع أمثلة الأكواد الواردة في هذا الدرس وتأكد من أن كل منها يعمل بشكل صحيح.
  2. قم بتعديل المثال الشامل وأضف الإضافات الخاصة بك
  3. راجع الوثائق الرسمية، وحدد واجهة برمجة تطبيقات (API) واحدة أو اثنتين لم يتم تناولهما في هذا الدرس، واكتب كود اختبار لهما.
  4. التأمل: كيف ستطبق ما تعلمته في هذا الدرس على مشروع في الواقع العملي؟
  5. حاول أن تجمع بين ما تعلمته في هذا الدرس والمواد التي درستها في الدروس السابقة لإنشاء مشروع صغير.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%