فارسی

بر فراخوانی‌های API نوع-ایمن در تایپ‌اسکریپت برای ساخت اپلیکیشن‌های وب قوی، قابل نگهداری و بدون خطا مسلط شوید. بهترین شیوه‌ها و تکنیک‌های پیشرفته را بیاموزید.

فراخوانی‌های API نوع-ایمن با تایپ‌اسکریپت: یک راهنمای جامع

در توسعه وب مدرن، تعامل با APIها یک وظیفه اساسی است. تایپ‌اسکریپت، با سیستم نوع قدرتمند خود، مزیت قابل توجهی در تضمین قابلیت اطمینان و نگهداری اپلیکیشن‌های شما با فعال کردن فراخوانی‌های API نوع-ایمن ارائه می‌دهد. این راهنما به بررسی چگونگی استفاده از ویژگی‌های تایپ‌اسکریپت برای ساخت تعاملات API قوی و بدون خطا، با پوشش بهترین شیوه‌ها، تکنیک‌های پیشرفته و مثال‌های واقعی می‌پردازد.

چرا ایمنی نوع برای فراخوانی‌های API اهمیت دارد

هنگام کار با APIها، شما اساساً با داده‌هایی سروکار دارید که از یک منبع خارجی می‌آیند. این داده‌ها ممکن است همیشه در قالبی که شما انتظار دارید نباشند، که منجر به خطاهای زمان اجرا و رفتارهای غیرمنتظره می‌شود. ایمنی نوع با تأیید اینکه داده‌هایی که دریافت می‌کنید با یک ساختار از پیش تعریف شده مطابقت دارند، یک لایه حفاظتی حیاتی فراهم می‌کند و مسائل بالقوه را در مراحل اولیه فرآیند توسعه شناسایی می‌کند.

راه‌اندازی پروژه تایپ‌اسکریپت شما

قبل از پرداختن به فراخوانی‌های API، اطمینان حاصل کنید که یک پروژه تایپ‌اسکریپت راه‌اندازی کرده‌اید. اگر از ابتدا شروع می‌کنید، می‌توانید یک پروژه جدید را با استفاده از دستورات زیر مقداردهی اولیه کنید:

npm init -y
npm install typescript --save-dev
tsc --init

این دستورات یک فایل `tsconfig.json` با گزینه‌های پیش‌فرض کامپایلر تایپ‌اسکریپت ایجاد می‌کند. شما می‌توانید این گزینه‌ها را مطابق با نیازهای پروژه خود سفارشی کنید. به عنوان مثال، ممکن است بخواهید حالت strict را برای بررسی نوع دقیق‌تر فعال کنید:

// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

تعریف انواع برای پاسخ‌های API

اولین قدم برای دستیابی به فراخوانی‌های API نوع-ایمن، تعریف انواع تایپ‌اسکریپت است که ساختار داده‌ای را که انتظار دارید از API دریافت کنید، نمایش می‌دهند. این کار معمولاً با استفاده از `interface` یا `type` انجام می‌شود.

استفاده از اینترفیس‌ها (Interfaces)

اینترفیس‌ها روشی قدرتمند برای تعریف شکل یک شیء هستند. به عنوان مثال، اگر در حال دریافت لیستی از کاربران از یک API هستید، ممکن است یک اینترفیس مانند این تعریف کنید:

interface User {
  id: number;
  name: string;
  email: string;
  address?: string; // پراپرتی اختیاری
  phone?: string; // پراپرتی اختیاری
  website?: string; // پراپرتی اختیاری
  company?: {
    name: string;
    catchPhrase: string;
    bs: string;
  };
}

علامت `?` بعد از نام یک پراپرتی نشان می‌دهد که آن پراپرتی اختیاری است. این برای مدیریت پاسخ‌های API که در آن‌ها ممکن است فیلدهای خاصی وجود نداشته باشند، مفید است.

استفاده از تایپ‌ها (Types)

تایپ‌ها شبیه به اینترفیس‌ها هستند اما انعطاف‌پذیری بیشتری ارائه می‌دهند، از جمله توانایی تعریف انواع union و intersection. شما می‌توانید با استفاده از یک تایپ به همان نتیجه اینترفیس بالا برسید:

type User = {
  id: number;
  name: string;
  email: string;
  address?: string; // پراپرتی اختیاری
  phone?: string; // پراپرتی اختیاری
  website?: string; // پراپرتی اختیاری
  company?: {
    name: string;
    catchPhrase: string;
    bs: string;
  };
};

برای ساختارهای ساده شیء، اینترفیس‌ها و تایپ‌ها اغلب قابل تعویض هستند. با این حال، تایپ‌ها هنگام کار با سناریوهای پیچیده‌تر، قدرتمندتر می‌شوند.

انجام فراخوانی‌های API با Axios

Axios یک کلاینت HTTP محبوب برای ارسال درخواست‌های API در جاوااسکریپت و تایپ‌اسکریپت است. این کتابخانه یک API تمیز و شهودی ارائه می‌دهد که مدیریت متدهای مختلف HTTP، هدرهای درخواست و داده‌های پاسخ را آسان می‌کند.

نصب Axios

npm install axios

انجام یک فراخوانی API نوع‌بندی شده

برای انجام یک فراخوانی API نوع-ایمن با Axios، می‌توانید از متد `axios.get` استفاده کرده و نوع پاسخ مورد انتظار را با استفاده از جنریک‌ها (generics) مشخص کنید:

import axios from 'axios';

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('خطا در دریافت کاربران:', error);
    throw error;
  }
}

fetchUsers().then(users => {
  users.forEach(user => {
    console.log(user.name);
  });
});

در این مثال، `axios.get('...')` به تایپ‌اسکریپت می‌گوید که داده‌های پاسخ انتظار می‌رود آرایه‌ای از اشیاء `User` باشد. این به تایپ‌اسکریپت اجازه می‌دهد تا هنگام کار با داده‌های پاسخ، بررسی نوع و تکمیل خودکار کد را ارائه دهد.

مدیریت متدهای مختلف HTTP

Axios از متدهای مختلف HTTP از جمله `GET`، `POST`، `PUT`، `DELETE` و `PATCH` پشتیبانی می‌کند. شما می‌توانید از متدهای مربوطه برای ارسال انواع مختلف درخواست‌های API استفاده کنید. به عنوان مثال، برای ایجاد یک کاربر جدید، ممکن است از متد `axios.post` استفاده کنید:

async function createUser(user: Omit): Promise {
  try {
    const response = await axios.post('https://jsonplaceholder.typicode.com/users', user);
    return response.data;
  } catch (error) {
    console.error('خطا در ایجاد کاربر:', error);
    throw error;
  }
}

const newUser = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  address: '123 Main St',
  phone: '555-1234',
  website: 'example.com',
  company: {
    name: 'Example Corp',
    catchPhrase: 'Leading the way',
    bs: 'Innovative solutions'
  }
};

createUser(newUser).then(user => {
  console.log('کاربر ایجاد شده:', user);
});

در این مثال، `Omit` یک نوع ایجاد می‌کند که همانند `User` است اما بدون پراپرتی `id`. این مفید است زیرا `id` معمولاً توسط سرور هنگام ایجاد یک کاربر جدید تولید می‌شود.

استفاده از Fetch API

Fetch API یک API داخلی جاوااسکریپت برای ارسال درخواست‌های HTTP است. در حالی که از Axios ساده‌تر است، می‌توان از آن نیز با تایپ‌اسکریپت برای دستیابی به فراخوانی‌های API نوع-ایمن استفاده کرد. اگر می‌خواهید از افزودن یک وابستگی جدید به پروژه خودداری کنید و نیازهای شما را برآورده می‌کند، ممکن است آن را ترجیح دهید.

انجام یک فراخوانی API نوع‌بندی شده با Fetch

برای انجام یک فراخوانی API نوع-ایمن با Fetch، می‌توانید از تابع `fetch` استفاده کرده و سپس پاسخ را به صورت JSON تجزیه کنید و نوع پاسخ مورد انتظار را مشخص نمایید:

async function fetchUsers(): Promise {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    if (!response.ok) {
      throw new Error(`خطای HTTP! وضعیت: ${response.status}`);
    }
    const data: User[] = await response.json();
    return data;
  } catch (error) {
    console.error('خطا در دریافت کاربران:', error);
    throw error;
  }
}

fetchUsers().then(users => {
  users.forEach(user => {
    console.log(user.name);
  });
});

در این مثال، `const data: User[] = await response.json();` به تایپ‌اسکریپت می‌گوید که داده‌های پاسخ باید به عنوان آرایه‌ای از اشیاء `User` در نظر گرفته شوند. این به تایپ‌اسکریپت اجازه می‌دهد تا بررسی نوع و تکمیل خودکار کد را انجام دهد.

مدیریت متدهای مختلف HTTP با Fetch

برای ارسال انواع مختلف درخواست‌های API با Fetch، می‌توانید از تابع `fetch` با گزینه‌های مختلفی مانند گزینه‌های `method` و `body` استفاده کنید. به عنوان مثال، برای ایجاد یک کاربر جدید، ممکن است از کد زیر استفاده کنید:

async function createUser(user: Omit): Promise {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(user)
    });
    if (!response.ok) {
      throw new Error(`خطای HTTP! وضعیت: ${response.status}`);
    }
    const data: User = await response.json();
    return data;
  } catch (error) {
    console.error('خطا در ایجاد کاربر:', error);
    throw error;
  }
}

const newUser = {
  name: 'John Doe',
  email: 'john.doe@example.com',
  address: '123 Main St',
  phone: '555-1234',
  website: 'example.com',
  company: {
    name: 'Example Corp',
    catchPhrase: 'Leading the way',
    bs: 'Innovative solutions'
  }
};

createUser(newUser).then(user => {
  console.log('کاربر ایجاد شده:', user);
});

مدیریت خطاهای API

مدیریت خطا یک جنبه حیاتی در فراخوانی‌های API است. APIها می‌توانند به دلایل زیادی از جمله مشکلات اتصال شبکه، خطاهای سرور و درخواست‌های نامعتبر با شکست مواجه شوند. ضروری است که این خطاها را به درستی مدیریت کنید تا از کرش کردن اپلیکیشن یا نمایش رفتارهای غیرمنتظره جلوگیری شود.

استفاده از بلوک‌های Try-Catch

رایج‌ترین روش برای مدیریت خطاها در کدهای ناهمزمان (asynchronous)، استفاده از بلوک‌های try-catch است. این به شما امکان می‌دهد تا هرگونه استثنائی (exception) که در طول فراخوانی API پرتاب می‌شود را گرفته و به طور مناسب مدیریت کنید.

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('خطا در دریافت کاربران:', error);
    // خطا را مدیریت کنید، به عنوان مثال، یک پیام خطا به کاربر نمایش دهید
    throw error; // خطا را دوباره پرتاب کنید تا کد فراخواننده نیز بتواند آن را مدیریت کند
  }
}

مدیریت کدهای خطای خاص

APIها اغلب کدهای خطای خاصی را برای نشان دادن نوع خطای رخ داده برمی‌گردانند. شما می‌توانید از این کدهای خطا برای ارائه مدیریت خطای خاص‌تر استفاده کنید. به عنوان مثال، ممکن است بخواهید برای خطای 404 Not Found پیام خطای متفاوتی نسبت به خطای 500 Internal Server Error نمایش دهید.

async function fetchUser(id: number): Promise {
  try {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      console.log(`کاربر با شناسه ${id} یافت نشد.`);
      return null; // یا یک خطای سفارشی پرتاب کنید
    } else {
      console.error('خطا در دریافت کاربر:', error);
      throw error;
    }
  }
}

fetchUser(123).then(user => {
  if (user) {
    console.log('کاربر:', user);
  } else {
    console.log('کاربر یافت نشد.');
  }
});

ایجاد انواع خطای سفارشی

برای سناریوهای مدیریت خطای پیچیده‌تر، می‌توانید انواع خطای سفارشی برای نمایش انواع مختلف خطاهای API ایجاد کنید. این به شما امکان می‌دهد اطلاعات خطای ساختاریافته‌تری ارائه دهید و خطاها را به طور مؤثرتری مدیریت کنید.

class ApiError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
    this.name = 'ApiError';
  }
}

async function fetchUser(id: number): Promise {
  try {
    const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      throw new ApiError(404, `کاربر با شناسه ${id} یافت نشد.`);
    } else {
      console.error('خطا در دریافت کاربر:', error);
      throw new ApiError(500, 'خطای داخلی سرور'); // یا هر کد وضعیت مناسب دیگری
    }
  }
}

fetchUser(123).catch(error => {
  if (error instanceof ApiError) {
    console.error(`خطای API: ${error.statusCode} - ${error.message}`);
  } else {
    console.error('یک خطای غیرمنتظره رخ داد:', error);
  }
});

اعتبارسنجی داده

حتی با وجود سیستم نوع تایپ‌اسکریپت، اعتبارسنجی داده‌هایی که از APIها در زمان اجرا دریافت می‌کنید، حیاتی است. APIها می‌توانند ساختار پاسخ خود را بدون اطلاع قبلی تغییر دهند و انواع تایپ‌اسکریپت شما ممکن است همیشه با پاسخ واقعی API کاملاً هماهنگ نباشند.

استفاده از Zod برای اعتبارسنجی زمان اجرا

Zod یک کتابخانه محبوب تایپ‌اسکریپت برای اعتبارسنجی داده در زمان اجرا است. این کتابخانه به شما امکان می‌دهد تا شِماهایی (schemas) را تعریف کنید که ساختار مورد انتظار داده‌های شما را توصیف می‌کنند و سپس داده‌ها را در زمان اجرا با آن شِماها اعتبارسنجی کنید.

نصب Zod

npm install zod

اعتبارسنجی پاسخ‌های API با Zod

برای اعتبارسنجی پاسخ‌های API با Zod، می‌توانید یک شِمای Zod تعریف کنید که با نوع تایپ‌اسکریپت شما مطابقت دارد و سپس از متد `parse` برای اعتبارسنجی داده‌ها استفاده کنید.

import { z } from 'zod';

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  address: z.string().optional(),
  phone: z.string().optional(),
  website: z.string().optional(),
  company: z.object({
    name: z.string(),
    catchPhrase: z.string(),
    bs: z.string(),
  }).optional(),
});

type User = z.infer;

async function fetchUsers(): Promise {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users');
    const data = z.array(userSchema).parse(response.data);
    return data;
  } catch (error) {
    console.error('خطا در دریافت کاربران:', error);
    throw error;
  }
}

در این مثال، `z.array(userSchema).parse(response.data)` اعتبارسنجی می‌کند که داده‌های پاسخ آرایه‌ای از اشیائی هستند که با `userSchema` مطابقت دارند. اگر داده‌ها با شِما مطابقت نداشته باشند، Zod یک خطا پرتاب می‌کند که می‌توانید آن را به درستی مدیریت کنید.

تکنیک‌های پیشرفته

استفاده از جنریک‌ها برای توابع API قابل استفاده مجدد

جنریک‌ها به شما امکان می‌دهند توابع API قابل استفاده مجددی بنویسید که می‌توانند انواع مختلف داده‌ها را مدیریت کنند. به عنوان مثال، می‌توانید یک تابع `fetchData` جنریک ایجاد کنید که می‌تواند داده‌ها را از هر نقطه پایانی (endpoint) API دریافت کرده و آن را با نوع صحیح برگرداند.

async function fetchData(url: string): Promise {
  try {
    const response = await axios.get(url);
    return response.data;
  } catch (error) {
    console.error(`خطا در دریافت داده از ${url}:`, error);
    throw error;
  }
}

// نحوه استفاده
fetchData('https://jsonplaceholder.typicode.com/users').then(users => {
  console.log('کاربران:', users);
});

fetchData<{ title: string; body: string }>('https://jsonplaceholder.typicode.com/todos/1').then(todo => {
    console.log('Todo', todo)
});

استفاده از Interceptorها برای مدیریت خطای سراسری

Axios رهگیرهایی (interceptors) را فراهم می‌کند که به شما امکان می‌دهند درخواست‌ها و پاسخ‌ها را قبل از اینکه توسط کد شما مدیریت شوند، رهگیری کنید. شما می‌توانید از interceptorها برای پیاده‌سازی مدیریت خطای سراسری، مانند ثبت خطاها یا نمایش پیام‌های خطا به کاربر، استفاده کنید.

axios.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error('مدیریت‌کننده خطای سراسری:', error);
    // نمایش یک پیام خطا به کاربر
    return Promise.reject(error);
  }
);

استفاده از متغیرهای محیطی برای URLهای API

برای جلوگیری از هاردکد کردن URLهای API در کد خود، می‌توانید از متغیرهای محیطی برای ذخیره URLها استفاده کنید. این کار پیکربندی اپلیکیشن شما را برای محیط‌های مختلف مانند توسعه، آزمایشی و پروداکشن آسان‌تر می‌کند.

مثال با استفاده از فایل `.env` و پکیج `dotenv`.

// .env
API_URL=https://api.example.com
// نصب dotenv
npm install dotenv
// وارد کردن و پیکربندی dotenv
import * as dotenv from 'dotenv'
dotenv.config()

const apiUrl = process.env.API_URL || 'http://localhost:3000'; // یک مقدار پیش‌فرض ارائه دهید

async function fetchData(endpoint: string): Promise {
  try {
    const response = await axios.get(`${apiUrl}/${endpoint}`);
    return response.data;
  } catch (error) {
    console.error(`خطا در دریافت داده از ${apiUrl}/${endpoint}:`, error);
    throw error;
  }
}

نتیجه‌گیری

فراخوانی‌های API نوع-ایمن برای ساخت اپلیکیشن‌های وب قوی، قابل نگهداری و بدون خطا ضروری هستند. تایپ‌اسکریپت ویژگی‌های قدرتمندی را فراهم می‌کند که به شما امکان می‌دهد انواع را برای پاسخ‌های API تعریف کنید، داده‌ها را در زمان اجرا اعتبارسنجی کنید و خطاها را به درستی مدیریت نمایید. با پیروی از بهترین شیوه‌ها و تکنیک‌های ذکر شده در این راهنما، می‌توانید کیفیت و قابلیت اطمینان تعاملات API خود را به طور قابل توجهی بهبود بخشید.

با استفاده از تایپ‌اسکریپت و کتابخانه‌هایی مانند Axios و Zod، می‌توانید اطمینان حاصل کنید که فراخوانی‌های API شما نوع-ایمن هستند، داده‌هایتان اعتبارسنجی شده و خطاهایتان به درستی مدیریت می‌شوند. این منجر به اپلیکیشن‌های قوی‌تر و قابل نگهداری‌تر خواهد شد.

به یاد داشته باشید که همیشه داده‌های خود را در زمان اجرا اعتبارسنجی کنید، حتی با وجود سیستم نوع تایپ‌اسکریپت. APIها می‌توانند تغییر کنند و انواع شما ممکن است همیشه با پاسخ واقعی API کاملاً هماهنگ نباشند. با اعتبارسنجی داده‌های خود در زمان اجرا، می‌توانید مشکلات بالقوه را قبل از اینکه در اپلیکیشن شما مشکل‌ساز شوند، شناسایی کنید.

کدنویسی خوشی داشته باشید!