קריאות שרת (Fetch API)

Fetch API מאפשרת לתקשר עם שרתים - לקבל ולשלוח נתונים דרך HTTP.


מה זה API?

API (Application Programming Interface) הוא ממשק שמאפשר לתוכנות לתקשר זו עם זו. בהקשר של ווב, מדובר בנקודות קצה (endpoints) בשרת שאפשר לפנות אליהן.

┌─────────────────┐    HTTP Request    ┌─────────────────┐
│                 │  ──────────────▶   │                 │
│    Frontend     │                    │    Backend      │
│   (JavaScript)  │  ◀──────────────   │    (Server)     │
│                 │    HTTP Response   │                 │
└─────────────────┘                    └─────────────────┘

HTTP Methods (שיטות)

Method שימוש דוגמה
GET קבלת נתונים לקבל רשימת משתמשים
POST יצירת נתון חדש יצירת משתמש חדש
PUT עדכון כל הנתון עדכון כל פרטי המשתמש
PATCH עדכון חלקי עדכון שם בלבד
DELETE מחיקה מחיקת משתמש

fetch בסיסי (GET)

// בקשת GET פשוטה
fetch("https://jsonplaceholder.typicode.com/users")
    .then(response => response.json())
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.error("שגיאה:", error);
    });

עם async/await

async function getUsers() {
    try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users");
        const users = await response.json();
        console.log(users);
    } catch (error) {
        console.error("שגיאה:", error);
    }
}

getUsers();

Response Object

האובייקט שחוזר מ-fetch מכיל מידע על התשובה:

async function checkResponse() {
    const response = await fetch("/api/users");

    console.log(response.status);      // 200, 404, 500...
    console.log(response.ok);          // true אם status 200-299
    console.log(response.statusText);  // "OK", "Not Found"...
    console.log(response.headers);     // Headers object

    // קריאת הגוף (אפשר רק פעם אחת!)
    const data = await response.json();
}

בדיקת תקינות התשובה

async function fetchData(url) {
    const response = await fetch(url);

    if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
    }

    return await response.json();
}

שליחת נתונים (POST)

async function createUser(user) {
    const response = await fetch("https://api.example.com/users", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(user)
    });

    const newUser = await response.json();
    return newUser;
}

// שימוש
const user = await createUser({
    name: "דן",
    email: "[email protected]"
});
console.log("נוצר:", user);

עדכון נתונים (PUT/PATCH)

PUT - החלפה מלאה

async function updateUser(id, userData) {
    const response = await fetch(`https://api.example.com/users/${id}`, {
        method: "PUT",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(userData)
    });

    return response.json();
}

PATCH - עדכון חלקי

async function updateUserEmail(id, email) {
    const response = await fetch(`https://api.example.com/users/${id}`, {
        method: "PATCH",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({ email })
    });

    return response.json();
}

מחיקה (DELETE)

async function deleteUser(id) {
    const response = await fetch(`https://api.example.com/users/${id}`, {
        method: "DELETE"
    });

    if (response.ok) {
        console.log("המשתמש נמחק");
    }
}

Headers

async function fetchWithAuth() {
    const response = await fetch("/api/protected-data", {
        headers: {
            "Content-Type": "application/json",
            "Authorization": "Bearer YOUR_TOKEN_HERE",
            "Accept-Language": "he"
        }
    });

    return response.json();
}

טיפול בשגיאות

async function fetchWithErrorHandling(url) {
    try {
        const response = await fetch(url);

        // בדיקת סטטוס
        if (!response.ok) {
            if (response.status === 404) {
                throw new Error("לא נמצא");
            } else if (response.status === 401) {
                throw new Error("לא מורשה");
            } else if (response.status >= 500) {
                throw new Error("שגיאת שרת");
            } else {
                throw new Error(`שגיאה: ${response.status}`);
            }
        }

        return await response.json();

    } catch (error) {
        // שגיאת רשת (אין חיבור)
        if (error.name === "TypeError") {
            console.error("בעיית חיבור לרשת");
        } else {
            console.error(error.message);
        }
        throw error;
    }
}

Query Parameters

// דרך 1: ידנית
const url = "https://api.example.com/users?page=1&limit=10";

// דרך 2: URLSearchParams
const params = new URLSearchParams({
    page: 1,
    limit: 10,
    sort: "name"
});
const url2 = `https://api.example.com/users?${params}`;
// "https://api.example.com/users?page=1&limit=10&sort=name"

// דרך 3: URL object
const url3 = new URL("https://api.example.com/users");
url3.searchParams.append("page", 1);
url3.searchParams.append("limit", 10);

AbortController - ביטול בקשה

const controller = new AbortController();

// ביטול אחרי 5 שניות
setTimeout(() => controller.abort(), 5000);

try {
    const response = await fetch("/api/data", {
        signal: controller.signal
    });
    const data = await response.json();
} catch (error) {
    if (error.name === "AbortError") {
        console.log("הבקשה בוטלה");
    } else {
        console.error("שגיאה:", error);
    }
}

FormData - שליחת טפסים

// מטופס קיים
const form = document.querySelector("#myForm");
const formData = new FormData(form);

await fetch("/api/submit", {
    method: "POST",
    body: formData  // לא צריך Content-Type header!
});

// ידנית
const data = new FormData();
data.append("name", "דן");
data.append("file", fileInput.files[0]);

await fetch("/api/upload", {
    method: "POST",
    body: data
});

דוגמה מלאה - CRUD Application

const API_URL = "https://jsonplaceholder.typicode.com";

// READ - קבלת כל המשתמשים
async function getUsers() {
    const response = await fetch(`${API_URL}/users`);
    return response.json();
}

// READ - קבלת משתמש ספציפי
async function getUser(id) {
    const response = await fetch(`${API_URL}/users/${id}`);
    if (!response.ok) throw new Error("משתמש לא נמצא");
    return response.json();
}

// CREATE - יצירת משתמש
async function createUser(userData) {
    const response = await fetch(`${API_URL}/users`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(userData)
    });
    return response.json();
}

// UPDATE - עדכון משתמש
async function updateUser(id, userData) {
    const response = await fetch(`${API_URL}/users/${id}`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(userData)
    });
    return response.json();
}

// DELETE - מחיקת משתמש
async function deleteUser(id) {
    const response = await fetch(`${API_URL}/users/${id}`, {
        method: "DELETE"
    });
    return response.ok;
}

// שימוש
async function main() {
    try {
        // קבלת כל המשתמשים
        const users = await getUsers();
        console.log("משתמשים:", users.length);

        // קבלת משתמש ספציפי
        const user = await getUser(1);
        console.log("משתמש:", user.name);

        // יצירה
        const newUser = await createUser({
            name: "דן ישראלי",
            email: "[email protected]"
        });
        console.log("נוצר:", newUser);

    } catch (error) {
        console.error("שגיאה:", error.message);
    }
}

main();

טעינת נתונים עם UI

async function loadUsersToPage() {
    const container = document.querySelector("#users");
    const loading = document.querySelector("#loading");
    const error = document.querySelector("#error");

    // הצגת טעינה
    loading.style.display = "block";
    error.style.display = "none";
    container.innerHTML = "";

    try {
        const response = await fetch("/api/users");

        if (!response.ok) {
            throw new Error("שגיאה בטעינת המשתמשים");
        }

        const users = await response.json();

        // בניית ה-HTML
        container.innerHTML = users.map(user => `
            <div class="user-card">
                <h3>${user.name}</h3>
                <p>${user.email}</p>
            </div>
        `).join("");

    } catch (err) {
        error.textContent = err.message;
        error.style.display = "block";

    } finally {
        loading.style.display = "none";
    }
}

// טעינה בעליית הדף
document.addEventListener("DOMContentLoaded", loadUsersToPage);

JSON

עבודה עם JSON

// המרה למחרוזת JSON
const obj = { name: "דן", age: 25 };
const jsonString = JSON.stringify(obj);
console.log(jsonString);  // '{"name":"דן","age":25}'

// המרה מ-JSON לאובייקט
const parsed = JSON.parse(jsonString);
console.log(parsed.name);  // "דן"

// JSON יפה (לקריאה)
const pretty = JSON.stringify(obj, null, 2);

XMLHttpRequest (לידע כללי)

הדרך הישנה לבקשות HTTP (לפני fetch):

// ❌ הדרך הישנה
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/users");
xhr.onload = function() {
    if (xhr.status === 200) {
        const data = JSON.parse(xhr.responseText);
        console.log(data);
    }
};
xhr.onerror = function() {
    console.error("שגיאה");
};
xhr.send();

// ✅ fetch מודרני
const response = await fetch("/api/users");
const data = await response.json();

סיכום

פעולה קוד
GET fetch(url)
POST fetch(url, { method: "POST", body: ... })
PUT fetch(url, { method: "PUT", body: ... })
DELETE fetch(url, { method: "DELETE" })
קריאת JSON response.json()
בדיקת תקינות response.ok או response.status
שליחת JSON body: JSON.stringify(data)
Headers headers: { "Content-Type": "application/json" }

כללי אצבע: - תמיד בדקו response.ok לפני עיבוד התשובה - השתמשו ב-try/catch לטיפול בשגיאות - השתמשו ב-async/await לקוד נקי - אל תשכחו את Content-Type header בשליחת JSON

בדקו את עצמכם

נסו לענות לבד לפני שאתם פותחים את התשובה.

  1. האם fetch זורק שגיאה על תשובה 404?

    1. לא: צריך לבדוק response.ok בעצמכם
    2. רק ב-POST
    3. כן, תמיד
    4. רק בשרת
    הצגת התשובה

    תשובה א. fetch נדחה רק על כשל רשת. 404 ו-500 הם תשובות תקינות מבחינתו.

  2. איך שולחים JSON ב-POST?

    1. fetch.post(url, data)
    2. fetch(url + data)
    3. fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) })
    4. fetch(url, { body: data })
    הצגת התשובה

    תשובה ג. צריך שיטה, header מתאים וגוף שהומר למחרוזת.