עברית

למדו לבצע קריאות API בטוחות-טיפוסים ב-TypeScript ליצירת יישומי רשת אמינים, קלים לתחזוקה וללא שגיאות. גלו שיטות עבודה מומלצות וטכניקות מתקדמות.

קריאות API בטוחות-טיפוסים עם TypeScript: מדריך מקיף

בפיתוח ווב מודרני, אינטראקציה עם ממשקי API היא משימה בסיסית. TypeScript, עם מערכת הטיפוסים החזקה שלה, מציעה יתרון משמעותי בהבטחת האמינות והתחזוקתיות של היישומים שלכם על ידי מתן אפשרות לקריאות API בטוחות-טיפוסים. מדריך זה יסקור כיצד למנף את התכונות של TypeScript לבניית אינטראקציות API אמינות וללא שגיאות, תוך כיסוי שיטות עבודה מומלצות, טכניקות מתקדמות ודוגמאות מהעולם האמיתי.

מדוע בטיחות טיפוסים חשובה לקריאות API

כאשר עובדים עם ממשקי API, אתם בעצם מתעסקים עם נתונים המגיעים ממקור חיצוני. נתונים אלו עשויים לא תמיד להיות בפורמט שאתם מצפים לו, מה שמוביל לשגיאות זמן ריצה והתנהגות בלתי צפויה. בטיחות טיפוסים מספקת שכבת הגנה חיונית על ידי אימות שהנתונים שאתם מקבלים תואמים למבנה שהוגדר מראש, ותופסת בעיות פוטנציאליות בשלב מוקדם בתהליך הפיתוח.

הגדרת פרויקט TypeScript

לפני שנצלול לקריאות API, ודאו שיש לכם פרויקט TypeScript מוגדר. אם אתם מתחילים מאפס, תוכלו לאתחל פרויקט חדש באמצעות:

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

פעולה זו תיצור קובץ `tsconfig.json` עם אפשרויות קומפיילר ברירת מחדל של TypeScript. תוכלו להתאים אישית אפשרויות אלו כדי שיתאימו לצרכי הפרויקט שלכם. לדוגמה, ייתכן שתרצו להפעיל מצב קפדני (strict mode) לבדיקת טיפוסים מחמירה יותר:

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

הגדרת טיפוסים לתגובות API

השלב הראשון בהשגת קריאות API בטוחות-טיפוסים הוא להגדיר טיפוסים של TypeScript המייצגים את מבנה הנתונים שאתם מצפים לקבל מה-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 ב-JavaScript ו-TypeScript. הוא מספק API נקי ואינטואיטיבי, המקל על הטיפול במתודות HTTP שונות, כותרות בקשה ונתוני תגובה.

התקנת Axios

npm install axios

ביצוע קריאת API עם טיפוסים

כדי לבצע קריאת API בטוחת-טיפוסים עם Axios, ניתן להשתמש במתודה `axios.get` ולציין את טיפוס התגובה הצפוי באמצעות ג'נריקס:

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('...')` אומר ל-TypeScript שנתוני התגובה צפויים להיות מערך של אובייקטי `User`. זה מאפשר ל-TypeScript לספק בדיקת טיפוסים והשלמה אוטומטית בעת עבודה עם נתוני התגובה.

טיפול במתודות 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 מובנה ב-JavaScript לביצוע בקשות HTTP. למרות שהוא בסיסי יותר מ-Axios, ניתן להשתמש בו גם עם TypeScript להשגת קריאות 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 error! status: ${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();` אומר ל-TypeScript שיש להתייחס לנתוני התגובה כמערך של אובייקטי `User`. זה מאפשר ל-TypeScript לבצע בדיקת טיפוסים והשלמה אוטומטית.

טיפול במתודות 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 error! status: ${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

הדרך הנפוצה ביותר לטפל בשגיאות בקוד אסינכרוני היא להשתמש בבלוקי 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);
  }
});

אימות נתונים

גם עם מערכת הטיפוסים של TypeScript, חיוני לאמת את הנתונים שאתם מקבלים מממשקי API בזמן ריצה. ממשקי API יכולים לשנות את מבנה התגובה שלהם ללא הודעה מוקדמת, והטיפוסים שלכם ב-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;

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` שיכולה לאחזר נתונים מכל נקודת קצה של 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)
});

שימוש ב-Interceptors לטיפול שגיאות גלובלי

Axios מספקת interceptors (מיירטים) המאפשרים ליירט בקשות ותגובות לפני שהן מטופלות על ידי הקוד שלכם. ניתן להשתמש ב-interceptors כדי ליישם טיפול שגיאות גלובלי, כגון רישום שגיאות או הצגת הודעות שגיאה למשתמש.

axios.interceptors.response.use(
  (response) => response,
  (error) => {
    console.error('מטפל שגיאות גלובלי:', error);
    // הציגו הודעת שגיאה למשתמש
    return Promise.reject(error);
  }
);

שימוש במשתני סביבה עבור כתובות URL של API

כדי להימנע מקידוד קשיח של כתובות URL של API בקוד שלכם, ניתן להשתמש במשתני סביבה לאחסון הכתובות. זה מקל על הגדרת היישום שלכם לסביבות שונות, כגון פיתוח, בדיקות (staging) ופרודקשן.

דוגמה באמצעות קובץ `.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 בטוחות-טיפוסים חיוניות לבניית יישומי רשת אמינים, קלים לתחזוקה וללא שגיאות. TypeScript מספקת תכונות חזקות המאפשרות לכם להגדיר טיפוסים לתגובות API, לאמת נתונים בזמן ריצה ולטפל בשגיאות בחן. על ידי ביצוע שיטות העבודה המומלצות והטכניקות המתוארות במדריך זה, תוכלו לשפר באופן משמעותי את איכות ואמינות אינטראקציות ה-API שלכם.

באמצעות שימוש ב-TypeScript וספריות כמו Axios ו-Zod, תוכלו להבטיח שקריאות ה-API שלכם בטוחות-טיפוסים, שהנתונים שלכם מאומתים, ושהשגיאות שלכם מטופלות בחן. זה יוביל ליישומים אמינים וקלים יותר לתחזוקה.

זכרו תמיד לאמת את הנתונים שלכם בזמן ריצה, גם עם מערכת הטיפוסים של TypeScript. ממשקי API יכולים להשתנות, והטיפוסים שלכם עשויים לא תמיד להיות מסונכרנים באופן מושלם עם התגובה האמיתית של ה-API. על ידי אימות הנתונים בזמן ריצה, תוכלו לתפוס בעיות פוטנציאליות לפני שהן גורמות לבעיות ביישום שלכם.

קידוד נעים!