دليل شامل لفهم كل شيء عن الـ APIs مع أمثلة
٢٦ مارس ٢٠٢٦
ملخص
تسمح واجهات برمجة التطبيقات (APIs) للبرامج بالتحدث مع بعضها البعض. يعتبر REST النمط الأكثر شيوعاً (HTTP + JSON)، ولكن GraphQL (لغة استعلام للبيانات)، وWebSockets (للوقت الفعلي)، وgRPC (RPC سريع) تلبي احتياجات مختلفة. تستخدم المصادقة الرموز (Bearer، مفاتيح API) أو OAuth لتفويض المستخدم. تعيد الأخطاء رموز الحالة (2xx للنجاح، 4xx لخطأ العميل، 5xx لخطأ الخادم). أتقن هذه الأنماط وستتمكن من استهلاك أي API بثقة.
تعد الـ API بمثابة عقد: "اتصل بي ببيانات X، وسأعطيك نتيجة Y". بدون واجهات برمجة التطبيقات، سيكون كل شيء معزولاً. بفضلها، يتحدث تطبيقك مع معالجي الدفع، وخدمات الخرائط، ونماذج الذكاء الاصطناعي، وقواعد البيانات. يغطي هذا الدليل النموذج الذهني (ما يحدث بالفعل)، والأنماط الأكثر شيوعاً (REST، GraphQL، WebSocket)، وأمثلة عملية يمكنك تشغيلها اليوم. سواء كنت تدمج خدمة خارجية أو تبني API ليستخدمها الآخرون، فإن فهم هذه الأنماط يجعلك مبرمجاً قوياً.
النموذج الذهني: ما هي الـ API؟
ببساطة: الـ API هي دالة (function) موجودة على خادم شخص آخر تستدعيها عبر الإنترنت.
// Local function (illustrative — never interpolate user input into SQL like this in real code; use parameterized queries)
function getUser(userId) {
return database.query('SELECT * FROM users WHERE id = ?', [userId]);
}
// API (same idea, but over HTTP)
fetch('https://API.example.com/users/123')
.then(res => res.json());
الفرق الرئيسي: في الدوال المحلية، تستدعيها مباشرة. أما مع واجهات برمجة التطبيقات، فأنت ترسل طلباً (request) عبر HTTP، ويرسل الخادم رداً (response).
كل طلب API يحتوي على:
- Method (أي إجراء: GET، POST، PUT، DELETE)
- Endpoint (المسار: /users/123)
- Headers (بيانات وصفية: المصادقة، نوع المحتوى)
- Body (حمولة بيانات اختيارية)
كل رد API يحتوي على:
- Status code (النجاح أو الفشل: 200، 404، 500)
- Headers (بيانات وصفية حول الرد)
- Body (البيانات الفعلية، عادةً بصيغة JSON)
REST: النمط الأكثر شيوعاً
تعد REST (Representational State Transfer) هي المعيار الصناعي. تستخدم أفعال HTTP لوصف الإجراءات على الموارد (الأسماء).
أساسيات REST
GET /posts → Fetch all posts
GET /posts/123 → Fetch post #123
POST /posts → Create a new post
PUT /posts/123 → Update post #123
DELETE /posts/123 → Delete post #123
لماذا تهم الأفعال والأسماء:
- GET آمن (لا يغير البيانات)
- DELETE خطير (يزيل البيانات)
- POST ينشئ؛ PUT يحدث
مثال REST: جلب البيانات
// Simple GET request
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
.then(data => console.log(data));
// Output: { id: 1, title: "...", body: "...", userId: 1 }
مثال REST: إنشاء بيانات (POST)
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'My Post',
body: 'This is the content',
userId: 1
})
})
.then(res => res.json())
.then(data => console.log(data));
// Output: { id: 101, title: "My Post", body: "This is the content", userId: 1 }
مثال REST: تحديث بيانات (PUT)
fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: 1,
title: 'Updated Title',
body: 'Updated body',
userId: 1
})
})
.then(res => res.json())
.then(data => console.log(data));
مثال REST: حذف بيانات
fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE'
})
.then(res => res.json())
.then(data => console.log('Deleted:', data));
GraphQL: لغة استعلام لواجهات برمجة التطبيقات
تسمح لك GraphQL بطلب البيانات التي تحتاجها بالضبط—لا أكثر ولا أقل.
REST مقابل GraphQL
REST:
GET /posts/1
→ { id, title, body, userId, createdAt, updatedAt, authorEmail, ... }
(you get everything; wasteful)
GraphQL:
query {
post(id: 1) {
title
body
author { name email }
}
}
→ { post: { title: "...", body: "...", author: { name: "...", email: "..." } } }
(you get only what you asked for)
مثال GraphQL
const query = `
query {
posts {
id
title
author {
name
}
}
}
`;
fetch('https://API.example.com/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
})
.then(res => res.json())
.then(data => console.log(data.data.posts));
المميزات:
- لا يوجد جلب زائد للبيانات (تحصل فقط على ما تحتاجه)
- لا يوجد جلب ناقص للبيانات (لا حاجة لعدة رحلات ذهاب وإياب)
- كتابة قوية (المخطط يصف ما هو متاح)
العيوب:
- أكثر تعقيداً في التعلم
- رفع الملفات قد يكون صعباً
- تعقيد الاستعلام يمكن أن يثقل كاهل الخوادم
WebSocket: الاتصال في الوقت الفعلي
يحافظ WebSocket على اتصال مفتوح للتحديثات في الوقت الفعلي (الدردشة، الإشعارات، البيانات الحية).
مثال WebSocket
const ws = new WebSocket('wss://API.example.com/live');
ws.addEventListener('open', () => {
console.log('Connected');
ws.send(JSON.stringify({ action: 'subscribe', channel: 'prices' }));
});
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Price update:', data);
});
ws.addEventListener('close', () => {
console.log('Disconnected');
});
حالات الاستخدام:
- الدردشة في الوقت الفعلي
- أسعار الأسهم المباشرة، العملات المشفرة
- الإشعارات المباشرة
- أدوات التعاون (مثل Google Docs)
الفرق عن REST:
- REST تعتمد على طلب-رد (العميل يسأل، الخادم يجيب، ينغلق الاتصال)
- WebSocket ثنائي الاتجاه (يمكن للعميل والخادم إرسال البيانات في أي وقت)
المصادقة: كيف تعرف واجهات برمجة التطبيقات من أنت
1. مفتاح API
رمز بسيط يتم إرساله مع كل طلب.
fetch('https://API.example.com/data', {
headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
})
المميزات: بسيط، يعمل للاتصال بين الخوادم العيوب: إذا تسرب، يمكن لأي شخص استخدامه
2. OAuth 2.0 (تفويض المستخدم)
يقوم المستخدم بتسجيل الدخول في تطبيقك، ويمنح الإذن للوصول إلى بياناته في خدمة أخرى (Gmail، GitHub، Spotify). لا يرى تطبيقك كلمة المرور الخاصة به أبداً.
// User clicks "Sign in with Google"
// Google authenticates user, sends your app a token
// Your app uses that token to fetch user's Google data
fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', {
headers: { 'Authorization': `Bearer ${googleToken` }
})
المميزات: يتحكم المستخدم في البيانات التي تصل إليها؛ لا تتعامل أبداً مع كلمات المرور العيوب: إعداد أكثر تعقيداً
3. ملفات تعريف الارتباط للجلسة (Session Cookies)
يقوم الخادم بتعيين ملف تعريف ارتباط؛ ويرسله المتصفح تلقائياً مع كل طلب.
// Login endpoint sets a cookie
fetch('https://API.example.com/login', {
method: 'POST',
credentials: 'include', // Send cookies
body: JSON.stringify({ username, password })
})
// Subsequent requests automatically include the cookie
fetch('https://API.example.com/me', {
credentials: 'include'
})
المميزات: يعمل بشكل جيد لتطبيقات الويب؛ يتم إرسال ملف تعريف الارتباط تلقائياً العيوب: هجمات CSRF ممكنة؛ أقل ملاءمة لعملاء الهاتف المحمول/واجهات برمجة التطبيقات
رموز حالة HTTP: فهم الردود
| الرمز | المعنى | مثال |
|---|---|---|
| 2xx | نجاح | |
| 200 | تم (نجح الطلب) | GET /posts ← يعيد المنشورات |
| 201 | تم الإنشاء (تم إنشاء مورد جديد) | POST /posts ← ينشئ منشوراً |
| 204 | لا يوجد محتوى (نجاح، لم يتم إرجاع بيانات) | DELETE /posts/1 |
| 3xx | إعادة توجيه | |
| 301 | تم النقل بشكل دائم | رابط قديم ← رابط جديد |
| 304 | لم يتم التعديل (الرد المخزن مؤقتاً سليم) | رأس If-Modified-Since |
| 4xx | خطأ عميل | |
| 400 | طلب سيئ (طلب غير صحيح) | حقل مطلوب مفقود |
| 401 | غير مصرح به (المصادقة مطلوبة) | نسيت مفتاح API |
| 403 | ممنوع (ليس لديك إذن) | غير المسؤول يحاول الوصول لمسار المسؤول |
| 404 | غير موجود (المورد غير موجود) | GET /posts/999 |
| 429 | طلبات كثيرة جداً (تجاوز الحد) | قمت بطلبات كثيرة بسرعة كبيرة |
| 5xx | خطأ خادم | |
| 500 | خطأ خادم داخلي (حدث عطل ما) | خطأ في كود الخادم |
| 503 | الخدمة غير متوفرة (متوقفة مؤقتاً) | صيانة أو ضغط زائد |
القاعدة الذهبية: إذا كانت الحالة 2xx، فقد نجح الطلب. إذا كانت 4xx أو 5xx، فهناك خطأ ما.
معالجة الأخطاء: عندما تسوء الأمور
async function fetchData(url) {
try {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status: ${res.statusText`);
}
return await res.json();
} catch (err) {
console.error('API error:', err.message);
// Handle gracefully: show user message, retry, etc.
}
}
سيناريوهات شائعة:
- خطأ في الشبكة: لا يمكن الوصول إلى الخادم (انتهاء الوقت، لا يوجد إنترنت). يساعد منطق إعادة المحاولة هنا.
- خطأ 4xx: طلب سيئ من جانبك (قم بتصحيح الطلب).
- خطأ 5xx: الخادم معطل (أعد المحاولة أو صعد المشكلة).
- تحديد المعدل (429): طلبات كثيرة جداً. انتظر قبل إعادة المحاولة.
تحديد المعدل: كن لطيفاً
غالباً ما تضع واجهات برمجة التطبيقات حداً للطلبات لمنع إساءة الاستخدام.
// Check headers for rate limit info
const remaining = res.headers.get('X-RateLimit-Remaining');
const resetTime = res.headers.get('X-RateLimit-Reset');
if (remaining === '0') {
console.log(`Rate limited. Reset at ${new Date(resetTime * 1000)`);
}
أفضل الممارسات: تحقق من الحصة المتبقية قبل كل طلب؛ وتراجع بلباقة عند الوصول للحد.
مثال واقعي: بناء أداة للبحث عن مستخدمي GitHub
async function getGitHubUser(username) {
const url = `https://API.GitHub.com/users/${username}`;
try {
const res = await fetch(url);
if (res.status === 404) {
throw new Error('User not found');
}
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const user = await res.json();
console.log(`${user.name} has ${user.public_repos} repos`);
} catch (err) {
console.error('Error:', err.message);
}
}
getGitHubUser('torvalds'); // Linus Torvalds
الخلاصة
تعد الـ APIs هي الطريقة التي تتواصل بها البرمجيات الحديثة. يهيمن REST لبساطته؛ ويتفوق GraphQL في المرونة؛ بينما يتيح WebSocket التواصل في الوقت الفعلي. تختلف طرق المصادقة حسب حالة الاستخدام (مفاتيح API، OAuth، ملفات تعريف الارتباط "cookies"). تخبرك أكواد الحالة بالنجاح أو الفشل؛ والتعامل مع الأخطاء يجعل تطبيقك مرنًا. مع إتقان هذه الأنماط، يمكنك دمج أي خدمة خارجية، وبناء APIs يستهلكها الآخرون، واستكشاف مشكلات الشبكة وإصلاحها بثقة. ابدأ بـ REST API بسيط (مثل JSONPlaceholder)، ثم انتقل إلى خدمات حقيقية (GitHub، Stripe، OpenAI) عندما تصبح أكثر تمكنًا.