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.
