Menu
CoddyTech

صفحة تستدعي API

جزء من قسم واجهات REST API في رحلة Node.js على Coddy. الدرس 21 من 25.

تستدعي صفحةٌ واجهة API باستخدام fetch. يحتاج طلب POST إلى ثلاثة خيارات: الطريقة والترويسات والنص الأساسي. حوّل كائن JavaScript إلى نص JSON لاستخدامه نصًا أساسيًا، وحدّد نوع ذلك النص بأنه application/json.

const response = await fetch('/api/books', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({title: title}),
});
const data = await response.json();

يستقبل await الأول استجابة HTTP. ويقرأ الثاني نص JSON الأساسي فيها. يخبرك response.ok ما إذا كانت حالتها ضمن النطاق الناجح. لا تؤدي حالة مثل 400 أو 409 إلى رفض وعد fetch؛ بل تعطيك استجابة تكون فيها قيمة ok هي false. تم توفير شيفرة النموذج والقائمة؛ ركّز على إرسال الطلب والتعامل مع نتيجته.

يشير المتغير message المتوفر إلى فقرة الرسالة في الصفحة. عيّن له textContent لعرض نص، أو سلسلة فارغة لمسحها:

message.textContent = data.error.message;
// امسحه بعد النجاح:
message.textContent = '';

تستخدم الصفحة وواجهة API المصدر نفسه، لذا لا يحتاج هذا الاستدعاء إلى إذن CORS. يشفّر FormData النموذج بطريقة تختلف عن JSON؛ استخدم نص JSON لأن واجهة API هذه تستخدم express.json().

challenge icon

التحدي

سهل

لا تحتوي واجهة API لديك على صفحة بعد. أضف إليها صفحة صغيرة يقدّمها الخادم نفسه، ويضيف نموذجها المهام من خلال واجهة API.

  1. في server.js، قدّم المجلد public باستخدام express.static، بحيث يعيد GET / الملف public/index.html.
  2. في public/index.html، يستخرج مستمع الإرسال العنوانَ بالفعل. اجعله يرسل المهمة الجديدة إلى واجهة API:
    • استخدم المتغير title المزوّد، والذي يحتوي على نص الإدخال.
    • أرسلها باستخدام fetch('/api/tasks', ...) على هيئة POST.
    • يكون المحتوى نص JSON مُنشأ باستخدام JSON.stringify، مثل {"title":"Water the plants"}.
    • يحتاج الطلب إلى الترويسة 'Content-Type': 'application/json' ضمن الخيار headers الخاص بـ fetch، وإلا فلن تتمكن express.json() من قراءة المحتوى.
  3. إذا كان الرد ناجحًا (تكون خاصية ok فيه true لأي حالة من 200 إلى 299)، فأفرغ النموذج باستخدام form.reset()، وأفرغ #message، واستدعِ load() كي تعرض القائمة المهمة الجديدة.
  4. وإلا، فسيكون محتوى الرد بالشكل الذي استخدمناه سابقًا للأخطاء، مثل:
    { "error": { "code": "conflict", "message": "A task with this title already exists" } }
    اعرض قيمة message في الفقرة #message.

تنفّذ الاختبارات نص الصفحة البرمجي المزوّد باستخدام نموذج مُحاكى صغير وواجهة API الحقيقية للدرس. وهي ترسل مهمة ومهمة مكررة، ثم تفحص القائمة والنموذج والرسالة. جرّب أيضًا الصفحة الفعلية بجانب المحرر.

جرّب ذلك: أرسل GET /. ستُفتح الصفحة في عرض الصفحة، وفيها المهام الثلاث الأولية. اكتب "Water the plants" واضغط Add: سيُفرغ حقل الإدخال وستظهر المهمة في أسفل القائمة. اكتب "Water the plants" مرة أخرى واضغط Add: ستبقى القائمة كما هي، وستظهر الرسالة "A task with this title already exists" أسفل النموذج.

جرّب بنفسك

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Tasks</title>
</head>
<body>
    <h1>Tasks</h1>
    <ul id="list"></ul>
    <form id="add">
        <input name="title" placeholder="New task" required>
        <button>Add</button>
    </form>
    <p id="message"></p>
    <script>
        const list = document.querySelector('#list');
        const form = document.querySelector('#add');
        const message = document.querySelector('#message');

        async function load() {
            const response = await fetch('/api/tasks?limit=50');
            const page = await response.json();
            list.replaceChildren(...page.items.map((task) => {
                const item = document.createElement('li');
                item.textContent = (task.done ? '[x] ' : '[ ] ') + task.title;
                return item;
            }));
        }

        form.addEventListener('submit', async (event) => {
            event.preventDefault();
            const title = form.elements.title.value;
            // Send the title to POST /api/tasks as JSON. On success clear
            // the form and load() again; on an error show its message.
        });

        load();
    </script>
</body>
</html>

جميع دروس واجهات REST API

تدرّب بنفسك: Playground لـ Express