חזרה למאמרים

מאמר מקצועי

API לעומת SDK לעומת CLI: איך לבחור ממשק

דוגמת localhost עובדת שמראה חוזה API, מעטפת SDK ופקודת CLI שמגיעים לאותו שירות.

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

שירות אחד, שלושה ממשקים

שמרו את הדוגמה המלאה בקובץ demo.mjs:

import { spawn } from "node:child_process";
import { createServer } from "node:http";
import { fileURLToPath } from "node:url";

async function requestBuckets(baseUrl) {
  const response = await fetch(`${baseUrl}/buckets`);
  if (!response.ok) throw new Error(`Bucket endpoint returned ${response.status}`);
  return response.json();
}

class BucketSdk {
  constructor(baseUrl) { this.baseUrl = baseUrl; }
  async listBuckets() { return (await requestBuckets(this.baseUrl)).buckets; }
}

async function runCli(baseUrl) {
  for (const bucket of await new BucketSdk(baseUrl).listBuckets()) console.log(bucket);
}

if (process.argv[2] === "--cli") {
  await runCli(process.argv[3]);
} else {
  const server = createServer((request, response) => {
    if (request.method !== "GET" || request.url !== "/buckets") {
      response.writeHead(404).end("Not found");
      return;
    }
    response.writeHead(200, { "content-type": "application/json" });
    response.end(JSON.stringify({ buckets: ["photos", "reports"] }));
  });
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
  const address = server.address();
  if (!address || typeof address === "string") throw new Error("Local service did not bind");
  const baseUrl = `http://127.0.0.1:${address.port}`;

  try {
    console.log(`API: ${JSON.stringify(await requestBuckets(baseUrl))}`);
    console.log(`SDK: ${(await new BucketSdk(baseUrl).listBuckets()).join(", ")}`);
    const child = spawn(process.execPath, [fileURLToPath(import.meta.url), "--cli", baseUrl], {
      stdio: ["ignore", "pipe", "inherit"]
    });
    let output = "";
    child.stdout.setEncoding("utf8");
    child.stdout.on("data", (chunk) => { output += chunk; });
    const exitCode = await new Promise((resolve) => child.once("close", resolve));
    if (exitCode !== 0) throw new Error(`CLI exited with ${exitCode}`);
    console.log(`CLI:\n${output.trim()}`);
  } finally {
    await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
  }
}

הריצו node demo.mjs. הפלט הצפוי:

API: {"buckets":["photos","reports"]}
SDK: photos, reports
CLI:
photos
reports

כשל אמיתי והתיקון שלו

בקשה אל /bucket מקבלת 404 Not found, כי החוזה מגדיר /buckets. מתקנים את הנתיב או את חוזה השירות. מעבר מ-API ל-SDK או ל-CLI אינו מבטל אימות, הרשאות, הגבלת קצב, ניסיונות חוזרים, חלוקה לעמודים או מגבלות שירות. כל ממשק צריך להתמודד עם אותם כללים בסיסיים.

לראות את הרעיון בסרטון

לצפייה בסרטון BetterMeir המקורי, EP008.

נבדק עם Node.js 26.0.0 ב-13 בספט׳ 2026.

מקורות ראשוניים