ارتباط با سرور و API

API و HTTP، Fetch API با Request و Response و Headers، متدهای HTTP، Query Parameters و Request Body، FormData و Blob و File، آپلود و دانلود، REST، CORS و AbortController.

API چیست؟

API (Application Programming Interface) قراردادی برای ارتباط برنامه‌هاست. در وب معمولاً REST API با HTTP:

// کلاینت (مرورگر) ←→ API ←→ دیتابیس

// ساختار درخواست:
{
    method: "GET",           // متد
    url: "/api/users",       // آدرس
    headers: { },            // سرصفحه‌ها
    body: { }                // داده ارسالی
}

HTTP برای جاوا اسکریپت

متدکارIdempotent
GETدریافت دادهبله
POSTایجادخیر
PUTجایگزینی کاملبله
PATCHتغییر جزئیخیر
DELETEحذفبله

کدهای وضعیت: 2xx موفق، 4xx خطای کلاینت، 5xx خطای سرور

Fetch API — دریافت داده

// ساده‌ترین GET:
const response = await fetch("/api/users");

// ⚠️ fetch برای 404 خطا نمی‌دهد!
console.log(response.ok);       // false برای 404
console.log(response.status);   // 404

// باید دستی چک کنیم:
if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
}

const data = await response.json();
console.log(data);

Request کامل — POST با بدنه

const response = await fetch("/api/users", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + token
    },
    body: JSON.stringify({
        name: "علی",
        email: "ali@example.com"
    })
});

const result = await response.json();

پاسخ — Response Object

const res = await fetch("/api/data");

console.log(res.status);      // 200
console.log(res.statusText);  // "OK"
console.log(res.ok);          // true
console.log(res.headers.get("Content-Type"));
console.log(res.url);         // آدرس نهایی (بعد redirect)

// انواع خواندن بدنه:
const json = await res.json();       // JSON
const text = await res.text();       // متن
const blob = await res.blob();       // فایل باینری
const buffer = await res.arrayBuffer(); // بایت‌ها

// فقط یکی قابل استفاده است!

Headers — سرصفحه‌ها

// با شیء Headers:
const headers = new Headers({
    "Content-Type": "application/json",
    "X-API-Key": "abc123"
});

headers.append("X-Custom", "value");
headers.set("Content-Type", "text/plain");
console.log(headers.get("Content-Type"));
headers.delete("X-Custom");

// در fetch:
fetch(url, { headers });

Query Parameters

// با URLSearchParams:
const params = new URLSearchParams({
    page: "1",
    limit: "10",
    sort: "name"
});

const res = await fetch(`/api/users?${params}`);

// داینامیک:
function buildUrl(base, params) {
    const url = new URL(base);
    Object.entries(params).forEach(([k, v]) => {
        if (v !== undefined && v !== null) {
            url.searchParams.set(k, v);
        }
    });
    return url.toString();
}

console.log(buildUrl("/api/search", { q: "js", page: 2 }));
// /api/search?q=js&page=2

FormData — فرم و فایل

// از فرم موجود:
const form = document.querySelector("#upload");
const formData = new FormData(form);

// دستی:
const fd = new FormData();
fd.append("name", "علی");
fd.append("avatar", fileInput.files[0]);   // فایل!
fd.append("docs", file2, "report.pdf");    // با نام دلخواه

// ارسال — Content-Type خودکار set می‌شود:
const res = await fetch("/api/upload", {
    method: "POST",
    body: formData
});

// پیمایش:
for (const [key, value] of fd.entries()) {
    console.log(key, value);
}

Blob و File

// Blob — داده باینری خام:
const blob = new Blob(["متن"], { type: "text/plain" });

// File — Blob با نام و تاریخ:
const file = new File(["محتوا"], "test.txt", {
    type: "text/plain",
    lastModified: Date.now()
});

console.log(file.name);
console.log(file.size);
console.log(file.type);

// تبدیل:
const text = await file.text();       // خواندن متن
const buffer = await file.arrayBuffer();
const url = URL.createObjectURL(blob); // لینک موقت

آپلود فایل با پیشرفت

// fetch پیشرفت آپلود ندارد — XMLHttpRequest دارد:
function uploadWithProgress(file, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open("POST", "/api/upload");

        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                const percent = (e.loaded / e.total) * 100;
                onProgress(percent);
            }
        };

        xhr.onload = () => resolve(JSON.parse(xhr.response));
        xhr.onerror = () => reject(new Error("خطای شبکه"));

        const fd = new FormData();
        fd.append("file", file);
        xhr.send(fd);
    });
}

// استفاده:
uploadWithProgress(file, percent => {
    console.log(`آپلود: ${percent.toFixed(0)}%`);
}).then(result => console.log("تمام!", result));

دانلود فایل

// دریافت باینری و دانلود:
async function downloadFile(url, filename) {
    const res = await fetch(url);
    const blob = await res.blob();

    // ساخت لینک دانلود:
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = filename;
    link.click();

    // پاک‌سازی:
    URL.revokeObjectURL(link.href);
}

downloadFile("/api/report.pdf", "گزارش.pdf");

REST API — الگوهای استاندارد

// CRUD با REST:
class ApiClient {
            #baseUrl;

            constructor(baseUrl) {
                this.#baseUrl = baseUrl;
            }

            // GET همه:
            async list(resource, params = {}) {
                const qs = new URLSearchParams(params);
                const res = await fetch(`${this.#baseUrl}/${resource}?${qs}`);
                return this.#handle(res);
            }

            // GET یکی:
            async get(resource, id) {
                return this.#handle(
                    await fetch(`${this.#baseUrl}/${resource}/${id}`)
                );
            }

            // POST ایجاد:
            async create(resource, data) {
                return this.#handle(await fetch(`${this.#baseUrl}/${resource}`, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data)
                }));
            }

            // PUT به‌روزرسانی:
            async update(resource, id, data) {
                return this.#handle(await fetch(`${this.#baseUrl}/${resource}/${id}`, {
                    method: "PUT",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data)
                }));
            }

            // DELETE:
            async remove(resource, id) {
                return this.#handle(await fetch(`${this.#baseUrl}/${resource}/${id}`, {
                    method: "DELETE"
                }));
            }

            async #handle(res) {
                if (!res.ok) {
                    const error = await res.json().catch(() => ({}));
                    throw new Error(error.message || `HTTP ${res.status}`);
                }
                return res.json();
            }
        }

const api = new ApiClient("https://api.example.com");
await api.create("users", { name: "علی" });
await api.list("users", { page: 1 });

احراز هویت API

// Bearer Token — رایج‌ترین:
fetch("/api/profile", {
    headers: {
        "Authorization": `Bearer ${accessToken}`
    }
});

// API Key:
fetch("/api/data", {
    headers: { "X-API-Key": "your-key" }
});

// توکن منقضی — Refresh:
async function authFetch(url, options = {}) {
    let token = localStorage.getItem("token");

    const res = await fetch(url, {
        ...options,
        headers: {
            ...options.headers,
            "Authorization": `Bearer ${token}`
        }
    });

    // 401 — refresh:
    if (res.status === 401) {
        const refreshRes = await fetch("/api/refresh", {
            method: "POST",
            body: localStorage.getItem("refreshToken")
        });
        const { token: newToken } = await refreshRes.json();
        localStorage.setItem("token", newToken);

        // تلاش مجدد:
        return authFetch(url, options);
    }

    return res;
}

AbortController — لغو درخواست

const controller = new AbortController();

fetch("/api/search?q=" + query, { signal: controller.signal })
    .then(r => r.json())
    .then(data => console.log(data))
    .catch(e => {
        if (e.name === "AbortError") {
            console.log("لغو شد!");
        }
    });

// لغو:
controller.abort();

// کاربرد — جستجوی زنده:
let controller;
searchInput.addEventListener("input", async (e) => {
    controller?.abort();  // قبلی را لغو کن
    controller = new AbortController();

    const res = await fetch(`/api/search?q=${e.target.value}`, {
        signal: controller.signal
    });
    const results = await res.json();
    renderResults(results);
});

CORS — اشتراک منابع

مرورگر درخواست بین‌دامنه‌ای را محدود می‌کند — سرور باید اجازه دهد:

// از site-a.com به api.site-b.com — CORS!
// سرور باید هدر بدهد:
// Access-Control-Allow-Origin: https://site-a.com

// درخواست‌های ساده مستقیم می‌روند.
// درخواست‌های پیچیده اول OPTIONS (preflight) می‌فرستند.

// خطای CORS در کلاینت قابل حل نیست —
// سرور باید هدرهای مناسب بفرستد!

XMLHttpRequest — روش قدیمی

// قبل از fetch — هنوز برای آپلود با پیشرفت:
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.onload = () => {
    if (xhr.status === 200) {
        console.log(JSON.parse(xhr.response));
    }
};
xhr.onerror = () => console.log("خطا");
xhr.send();

جمع‌بندی

کارابزار
GET/POST سادهfetch
JSONJSON.stringify/parse
فایل/فرمFormData
پیشرفت آپلودXMLHttpRequest
لغوAbortController
queryURLSearchParams

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط