العربية

أتقن استدعاءات API الآمنة من النوع في TypeScript لتطبيقات الويب القوية والقابلة للصيانة والخالية من الأخطاء. تعلم أفضل الممارسات والتقنيات المتقدمة.

استدعاءات واجهة برمجة التطبيقات (API) الآمنة من النوع باستخدام TypeScript: دليل شامل

في تطوير الويب الحديث، يعد التفاعل مع واجهات برمجة التطبيقات (APIs) مهمة أساسية. تقدم TypeScript، بنظامها النوعي القوي، ميزة كبيرة في ضمان موثوقية وقابلية صيانة تطبيقاتك من خلال تمكين استدعاءات API الآمنة من النوع. سيوضح هذا الدليل كيفية الاستفادة من ميزات TypeScript لبناء تفاعلات API قوية وخالية من الأخطاء، وتغطية أفضل الممارسات والتقنيات المتقدمة والأمثلة الواقعية.

لماذا يهم أمان النوع لاستدعاءات API

عند العمل مع واجهات برمجة التطبيقات (APIs)، فإنك تتعامل بشكل أساسي مع البيانات القادمة من مصدر خارجي. قد لا تكون هذه البيانات دائمًا بالتنسيق الذي تتوقعه، مما يؤدي إلى أخطاء وقت التشغيل والسلوك غير المتوقع. يوفر أمان النوع طبقة حماية حاسمة من خلال التحقق من أن البيانات التي تتلقاها تتوافق مع هيكل محدد مسبقًا، واكتشاف المشكلات المحتملة في وقت مبكر من عملية التطوير.

إعداد مشروع TypeScript الخاص بك

قبل الغوص في استدعاءات API، تأكد من أن لديك مشروع TypeScript مُعدًا. إذا كنت تبدأ من الصفر، فيمكنك تهيئة مشروع جديد باستخدام:

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

سيؤدي هذا إلى إنشاء ملف `tsconfig.json` مع خيارات مترجم TypeScript الافتراضية. يمكنك تخصيص هذه الخيارات لتناسب احتياجات مشروعك. على سبيل المثال، قد ترغب في تمكين الوضع الصارم لإجراء فحص نوع أكثر صرامة:

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

تحديد أنواع استجابات API

الخطوة الأولى في تحقيق استدعاءات API الآمنة من النوع هي تحديد أنواع TypeScript التي تمثل هيكل البيانات التي تتوقع تلقيها من واجهة برمجة التطبيقات (API). يتم ذلك عادةً باستخدام إعلانات `interface` أو `type`.

استخدام الواجهات

الواجهات هي طريقة قوية لتحديد شكل الكائن. على سبيل المثال، إذا كنت تجلب قائمة بالمستخدمين من واجهة برمجة تطبيقات (API)، فقد تحدد واجهة مثل هذه:

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

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

استخدام الأنواع

الأنواع مشابهة للواجهات ولكنها توفر مزيدًا من المرونة، بما في ذلك القدرة على تحديد أنواع الاتحاد وأنواع التقاطع. يمكنك تحقيق نفس النتيجة مثل الواجهة أعلاه باستخدام نوع:

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 في JavaScript و TypeScript. يوفر واجهة برمجة تطبيقات نظيفة وبديهية، مما يسهل التعامل مع طرق HTTP المختلفة ورؤوس الطلبات وبيانات الاستجابة.

تثبيت Axios

npm install axios

إجراء استدعاء API مكتوب

لإجراء استدعاء API آمن من النوع باستخدام Axios، يمكنك استخدام طريقة `axios.get` وتحديد نوع الاستجابة المتوقع باستخدام الأنواع العامة:

import axios from 'axios';

async function fetchUsers(): Promise<User[]> {
  try {
    const response = await axios.get<User[]>('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
}

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

في هذا المثال، يخبر `axios.get<User[]>('...')` TypeScript أن بيانات الاستجابة من المتوقع أن تكون مصفوفة من كائنات `User`. يسمح هذا لـ TypeScript بتوفير التحقق من النوع والإكمال التلقائي عند العمل مع بيانات الاستجابة.

التعامل مع طرق HTTP المختلفة

يدعم Axios طرق HTTP المختلفة، بما في ذلك `GET` و `POST` و `PUT` و `DELETE` و `PATCH`. يمكنك استخدام الطرق المقابلة لإجراء أنواع مختلفة من طلبات API. على سبيل المثال، لإنشاء مستخدم جديد، يمكنك استخدام طريقة `axios.post`:

async function createUser(user: Omit<User, 'id'>): Promise<User> {
  try {
    const response = await axios.post<User>('https://jsonplaceholder.typicode.com/users', user);
    return response.data;
  } catch (error) {
    console.error('Error creating user:', 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('Created user:', user);
});

في هذا المثال، يقوم `Omit<User, 'id'>` بإنشاء نوع مماثل لـ `User` ولكن بدون خاصية `id`. هذا مفيد لأن `id` يتم إنشاؤه عادةً بواسطة الخادم عند إنشاء مستخدم جديد.

استخدام Fetch API

Fetch API هي واجهة برمجة تطبيقات JavaScript مضمنة لإجراء طلبات HTTP. في حين أنها أكثر أساسية من Axios، إلا أنه يمكن استخدامها أيضًا مع TypeScript لتحقيق استدعاءات API آمنة من النوع. قد تفضلها لتجنب إضافة تبعية إذا كانت تناسب احتياجاتك.

إجراء استدعاء API مكتوب باستخدام Fetch

لإجراء استدعاء API آمن من النوع باستخدام Fetch، يمكنك استخدام دالة `fetch` ثم تحليل الاستجابة كـ JSON، مع تحديد نوع الاستجابة المتوقعة:

async function fetchUsers(): Promise<User[]> {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data: User[] = await response.json();
    return data;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
}

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

في هذا المثال، يخبر `const data: User[] = await response.json();` TypeScript أنه يجب التعامل مع بيانات الاستجابة كمصفوفة من كائنات `User`. يسمح هذا لـ TypeScript بإجراء فحص النوع والإكمال التلقائي.

التعامل مع طرق HTTP المختلفة باستخدام Fetch

لإجراء أنواع مختلفة من طلبات API باستخدام Fetch، يمكنك استخدام دالة `fetch` مع خيارات مختلفة، مثل خيارات `method` و `body`. على سبيل المثال، لإنشاء مستخدم جديد، يمكنك استخدام الكود التالي:

async function createUser(user: Omit<User, 'id'>): Promise<User> {
  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 error! status: ${response.status}`);
    }
    const data: User = await response.json();
    return data;
  } catch (error) {
    console.error('Error creating user:', 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('Created user:', user);
});

معالجة أخطاء API

تعد معالجة الأخطاء جانبًا مهمًا لاستدعاءات API. يمكن أن تفشل واجهات برمجة التطبيقات (APIs) لأسباب عديدة، بما في ذلك مشكلات اتصال الشبكة وأخطاء الخادم والطلبات غير الصالحة. من الضروري التعامل مع هذه الأخطاء بأناقة لمنع تعطل تطبيقك أو عرض سلوك غير متوقع.

استخدام كتل Try-Catch

الطريقة الأكثر شيوعًا للتعامل مع الأخطاء في التعليمات البرمجية غير المتزامنة هي استخدام كتل try-catch. يتيح لك ذلك التقاط أي استثناءات يتم طرحها أثناء استدعاء API والتعامل معها بشكل مناسب.

async function fetchUsers(): Promise<User[]> {
  try {
    const response = await axios.get<User[]>('https://jsonplaceholder.typicode.com/users');
    return response.data;
  } catch (error) {
    console.error('Error fetching users:', error);
    // تعامل مع الخطأ، على سبيل المثال، عرض رسالة خطأ للمستخدم
    throw error; // أعد طرح الخطأ للسماح للتعليمات البرمجية المتصلة بالتعامل معه أيضًا
  }
}

التعامل مع رموز خطأ معينة

غالبًا ما تُرجع واجهات برمجة التطبيقات (APIs) رموز خطأ معينة للإشارة إلى نوع الخطأ الذي حدث. يمكنك استخدام رموز الخطأ هذه لتوفير معالجة خطأ أكثر تحديدًا. على سبيل المثال، قد ترغب في عرض رسالة خطأ مختلفة لخطأ 404 لم يتم العثور عليه أكثر من خطأ خادم داخلي 500.

async function fetchUser(id: number): Promise<User | null> {
  try {
    const response = await axios.get<User>(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      console.log(`User with ID ${id} not found.`);
      return null; // أو قم بطرح خطأ مخصص
    } else {
      console.error('Error fetching user:', error);
      throw error;
    }
  }
}

fetchUser(123).then(user => {
  if (user) {
    console.log('User:', user);
  } else {
    console.log('User not found.');
  }
});

إنشاء أنواع أخطاء مخصصة

بالنسبة لسيناريوهات معالجة الأخطاء الأكثر تعقيدًا، يمكنك إنشاء أنواع أخطاء مخصصة لتمثيل أنواع مختلفة من أخطاء API. يتيح لك ذلك توفير معلومات خطأ منظمة بشكل أكبر والتعامل مع الأخطاء بشكل أكثر فعالية.

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

async function fetchUser(id: number): Promise<User> {
  try {
    const response = await axios.get<User>(`https://jsonplaceholder.typicode.com/users/${id}`);
    return response.data;
  } catch (error: any) {
    if (error.response?.status === 404) {
      throw new ApiError(404, `User with ID ${id} not found.`);
    } else {
      console.error('Error fetching user:', error);
      throw new ApiError(500, 'Internal Server Error'); // أو أي رمز حالة مناسب آخر
    }
  }
}

fetchUser(123).catch(error => {
  if (error instanceof ApiError) {
    console.error(`API Error: ${error.statusCode} - ${error.message}`);
  } else {
    console.error('An unexpected error occurred:', error);
  }
});

التحقق من صحة البيانات

حتى مع نظام نوع TypeScript، من الضروري التحقق من صحة البيانات التي تتلقاها من واجهات برمجة التطبيقات (APIs) في وقت التشغيل. يمكن لواجهات برمجة التطبيقات (APIs) تغيير هيكل الاستجابة الخاصة بها دون إشعار، وقد لا تتم مزامنة أنواع TypeScript الخاصة بك دائمًا بشكل مثالي مع الاستجابة الفعلية لواجهة برمجة التطبيقات (API).

استخدام Zod للتحقق من صحة وقت التشغيل

Zod هي مكتبة TypeScript شائعة للتحقق من صحة البيانات في وقت التشغيل. يسمح لك بتعريف مخططات تصف الهيكل المتوقع لبياناتك ثم التحقق من صحة البيانات مقابل هذه المخططات في وقت التشغيل.

تثبيت Zod

npm install zod

التحقق من صحة استجابات API باستخدام Zod

للتحقق من صحة استجابات API باستخدام Zod، يمكنك تعريف مخطط Zod يتوافق مع نوع TypeScript الخاص بك ثم استخدام طريقة `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<typeof userSchema>;

async function fetchUsers(): Promise<User[]> {
  try {
    const response = await axios.get<unknown>('https://jsonplaceholder.typicode.com/users');
    const data = z.array(userSchema).parse(response.data);
    return data;
  } catch (error) {
    console.error('Error fetching users:', error);
    throw error;
  }
}

في هذا المثال، يتحقق `z.array(userSchema).parse(response.data)` من أن بيانات الاستجابة عبارة عن مصفوفة من الكائنات التي تتوافق مع `userSchema`. إذا لم تتوافق البيانات مع المخطط، فسوف يطرح Zod خطأ، والذي يمكنك بعد ذلك التعامل معه بشكل مناسب.

التقنيات المتقدمة

استخدام الأنواع العامة لوظائف API القابلة لإعادة الاستخدام

تتيح لك الأنواع العامة كتابة وظائف API قابلة لإعادة الاستخدام يمكنها التعامل مع أنواع مختلفة من البيانات. على سبيل المثال، يمكنك إنشاء دالة `fetchData` عامة يمكنها جلب البيانات من أي نقطة نهاية API وإعادتها بالنوع الصحيح.

async function fetchData<T>(url: string): Promise<T> {
  try {
    const response = await axios.get<T>(url);
    return response.data;
  } catch (error) {
    console.error(`Error fetching data from ${url}:`, error);
    throw error;
  }
}

// الاستخدام
fetchData<User[]>('https://jsonplaceholder.typicode.com/users').then(users => {
  console.log('Users:', users);
});

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

استخدام المعترِضات لمعالجة الأخطاء العامة

يوفر Axios معترِضات تتيح لك اعتراض الطلبات والاستجابات قبل التعامل معها بواسطة التعليمات البرمجية الخاصة بك. يمكنك استخدام المعترِضات لتنفيذ معالجة الأخطاء العامة، مثل تسجيل الأخطاء أو عرض رسائل الخطأ للمستخدم.

axios.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error('Global error handler:', 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<T>(endpoint: string): Promise<T> {
  try {
    const response = await axios.get<T>(`${apiUrl}/${endpoint}`);
    return response.data;
  } catch (error) {
    console.error(`Error fetching data from ${apiUrl}/${endpoint}:`, error);
    throw error;
  }
}

الخلاصة

تعتبر استدعاءات API الآمنة من النوع ضرورية لبناء تطبيقات ويب قوية وقابلة للصيانة وخالية من الأخطاء. يوفر TypeScript ميزات قوية تمكنك من تحديد أنواع لاستجابات API، والتحقق من صحة البيانات في وقت التشغيل، والتعامل مع الأخطاء بأناقة. باتباع أفضل الممارسات والتقنيات الموضحة في هذا الدليل، يمكنك تحسين جودة وموثوقية تفاعلات API الخاصة بك بشكل كبير.

باستخدام TypeScript والمكتبات مثل Axios و Zod، يمكنك التأكد من أن استدعاءات API الخاصة بك آمنة من النوع، وأن بياناتك تم التحقق من صحتها، وأن أخطائك يتم التعامل معها بأناقة. سيؤدي هذا إلى تطبيقات أكثر قوة وقابلة للصيانة.

تذكر دائمًا التحقق من صحة بياناتك في وقت التشغيل، حتى مع نظام نوع TypeScript. يمكن لواجهات برمجة التطبيقات (APIs) أن تتغير، وقد لا تتم مزامنة أنواعك دائمًا بشكل مثالي مع الاستجابة الفعلية لواجهة برمجة التطبيقات (API). من خلال التحقق من صحة بياناتك في وقت التشغيل، يمكنك اكتشاف المشكلات المحتملة قبل أن تتسبب في مشاكل في تطبيقك.

برمجة ممتعة!

استدعاءات واجهة برمجة التطبيقات (API) الآمنة من النوع باستخدام TypeScript: دليل شامل | MLOG