batch — כמה פעולות בבקשה אחת
batch אורז כמה פעולות CRUD לבקשת HTTP אחת. הרווח הוא בעיקר בזמן רשת: במקום 50 סבבי הלוך‑חזור, אחד.
מה batch הוא לא: הוא אינו טרנזקציה. אין rollback, אין אטומיות. כל פעולה נכשלת או מצליחה בנפרד, וההצלחות נשארות.
מבנה הבקשה
Section titled “מבנה הבקשה”{ "requests": [ { "method": "POST", "path": "/parse/classes/Accounts", "body": { "Name": "לקוח א", "PhoneNumber": "0501111111", "IsAccount": false } }, { "method": "PUT", "path": "/parse/classes/Accounts/xK9mP2qRsT", "body": { "IsAccount": true } }, { "method": "DELETE", "path": "/parse/classes/Accounts/Cpl9lrueY5" } ]}| שדה | חובה | הערות |
|---|---|---|
requests |
✔ | מערך. המלצת המוצר: עד 50 איברים — לא נאכף, ראו למטה |
requests[].method |
✔ | POST · PUT · DELETE |
requests[].path |
✔ | הנתיב המלא כולל /parse — למשל /parse/classes/Tasks |
requests[].body |
ל‑POST ו‑PUT | אותו גוף בדיוק כמו בקריאה בודדת, כולל עטיפות Pointer ו‑Date |
הכותרות (X-Parse-Application-Id ואישור ההרשאה) נשלחות פעם אחת, על הבקשה כולה.
curl -sS -X POST "https://api.mbapps.co.il/parse/batch" \ -H "X-Parse-Application-Id: $APP_ID" \ -H "X-Parse-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"requests":[ {"method":"POST","path":"/parse/classes/Tasks", "body":{"Name":"התקשר ללקוח", "AccountId":{"__type":"Pointer","className":"Accounts","objectId":"xK9mP2qRsT"}}} ]}'const APP_ID = process.env.APP_ID, API_KEY = process.env.API_KEY;
const res = await fetch('https://api.mbapps.co.il/parse/batch', { method: 'POST', headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-API-Key': API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ requests: [ { method: 'POST', path: '/parse/classes/Tasks', body: { Name: 'התקשר ללקוח', AccountId: { __type: 'Pointer', className: 'Accounts', objectId: 'xK9mP2qRsT' }, }, }, ], }),});const data = await res.json();import os, requests
APP_ID = os.environ["APP_ID"]API_KEY = os.environ["API_KEY"]
res = requests.post( "https://api.mbapps.co.il/parse/batch", headers={ "X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, "Content-Type": "application/json", }, json={ "requests": [ { "method": "POST", "path": "/parse/classes/Tasks", "body": { "Name": "התקשר ללקוח", "AccountId": {"__type": "Pointer", "className": "Accounts", "objectId": "xK9mP2qRsT"}, }, }, ], },)data = res.json()<?php$appId = getenv('APP_ID');$apiKey = getenv('API_KEY');
$ch = curl_init();curl_setopt_array($ch, [ CURLOPT_URL => "https://api.mbapps.co.il/parse/batch", CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "X-Parse-Application-Id: $appId", "X-Parse-API-Key: $apiKey", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "requests" => [ [ "method" => "POST", "path" => "/parse/classes/Tasks", "body" => [ "Name" => "התקשר ללקוח", "AccountId" => ["__type" => "Pointer", "className" => "Accounts", "objectId" => "xK9mP2qRsT"], ], ], ], ], JSON_UNESCAPED_UNICODE),]);$response = curl_exec($ch);curl_close($ch);$data = json_decode($response, true);מבנה התשובה — וזו הנקודה החשובה
Section titled “מבנה התשובה — וזו הנקודה החשובה”התשובה היא מערך באותו סדר של הבקשות. כל איבר מכיל success או error:
[ { "success": { "objectId": "aB3dEf9HiJ", "createdAt": "2026-08-20T09:00:00.000Z" } }, { "success": { "updatedAt": "2026-08-20T09:00:00.100Z", "objectId": "xK9mP2qRsT" } }, { "error": { "code": 101, "error": "Object not found." } }]טיפול נכון בתשובה
Section titled “טיפול נכון בתשובה”import requests
API = "https://api.mbapps.co.il/parse"H = {"X-Parse-Application-Id": APP_ID, "X-Parse-API-Key": API_KEY, "Content-Type": "application/json"}
def run_batch(operations: list[dict]) -> tuple[list, list]: """מריץ אצווה ומחזיר (הצלחות, כישלונות-עם-הבקשה-המקורית).""" assert len(operations) <= 50, "גודל אצווה מומלץ: 50 (השרת לא יעצור אתכם)" r = requests.post(f"{API}/batch", headers=H, json={"requests": operations}, timeout=60) r.raise_for_status() # תופס רק כשל של הבקשה כולה ok, failed = [], [] for op, result in zip(operations, r.json()): if "success" in result: ok.append(result["success"]) else: failed.append({"request": op, "error": result.get("error")}) return ok, failedחיתוך מערכים גדולים
Section titled “חיתוך מערכים גדולים”חתכו בצד הלקוח:
import time
def chunks(seq, size=50): for i in range(0, len(seq), size): yield seq[i:i + size]
def create_all(table: str, rows: list[dict], pause: float = 0.3): ops = [{"method": "POST", "path": f"/parse/classes/{table}", "body": row} for row in rows] created, errors = [], [] for group in chunks(ops, 50): ok, bad = run_batch(group) created += ok errors += bad time.sleep(pause) # אין מגבלות קצב מתועדות — האטה מרצון return created, errorsטריגרים — המלכודת האמיתית של batch
Section titled “טריגרים — המלכודת האמיתית של batch”ל‑POST /parse/batch אין דגל להשתקת טריגרים. אין skipTriggers, אין skipTimeline — דגלים כאלה ברמת הבקשה פשוט מתעלמים מהם: batch עם "skipTriggers":true מפעיל את הטריגר על כל רשומה, וכל רשומה מקבלת 3 שורות _Timeline.
מה כן אפשר:
| הדרך | איך | ההגבלה |
|---|---|---|
| כיבוי זמני של הטריגר | סביבת פיתוח ← Databases ← המערכת ← Triggers — לכל טריגר יש עמודת Active. דרך MCP: Set-Trigger עם "active": false |
דורש תיאום עם מי שמחזיק את האוטומציות; אל תשכחו להדליק חזרה |
| תיאום מראש | להריץ בשעה שבה שליחה החוצה לא מזיקה | הפתרון הפחות אלגנטי והכי בטוח |
הכלי Create-Many של MCP |
{"table":"Accounts","data":[…],"skipTriggers":true,"skipTimeline":true} |
שני הדגלים מכובדים רק באימות Master Key. בגרסאות שרת ישנות הכלי היה חסום, ראו למטה |
שתי טעויות שחוזרות
Section titled “שתי טעויות שחוזרות”1 · הנתיב חייב לכלול /parse
Section titled “1 · הנתיב חייב לכלול /parse”{ "method": "POST", "path": "/classes/Accounts" } // ✘ 400 {"code":107,"error":"cannot route batch path /classes/Accounts"}{ "method": "POST", "path": "/parse/classes/Accounts" } // ✔ושגיאות מבנה נוספות — חלקן מפילות את כל הבקשה (400), וחלקן חוזרות כ‑error של איבר בודד בתוך 200:
| הבקשה | התשובה |
|---|---|
requests חסר או אינו מערך |
400 — {"code":107,"error":"requests must be an array"} |
method שאינו POST/PUT/DELETE (למשל PATCH) |
400 — {"code":107,"error":"cannot route PATCH /classes/…"} |
GET בתוך batch |
200, והאיבר: {"error":{"error":"Cannot convert undefined or null to object"}} |
POST בלי body |
200, והאיבר: אותה שגיאה — Cannot convert undefined or null to object |
requests: [] |
200 עם [] |
2 · הגוף הוא אותו גוף — כולל העטיפות
Section titled “2 · הגוף הוא אותו גוף — כולל העטיפות”batch אינו שכבה חדשה. Pointer עדיין אובייקט עטוף, ותאריך עדיין {"__type":"Date","iso":…}:
{ "method": "POST", "path": "/parse/classes/Tasks", "body": { "Name": "מעקב הצעת מחיר", "Date": { "__type": "Date", "iso": "2026-09-01T08:00:00.000Z" }, "AccountId": { "__type": "Pointer", "className": "Accounts", "objectId": "xK9mP2qRsT" } }}מתי לא להשתמש ב‑batch
Section titled “מתי לא להשתמש ב‑batch”| המצב | מה עדיף |
|---|---|
| הפעולות תלויות זו בזו (יצירת לקוח ואז משימה שמקושרת אליו) | שתי קריאות רצופות — ה‑objectId מהראשונה נחוץ לשנייה, ו‑batch לא מזין תוצאות ביניהן |
| צריך אטומיות (“הכול או כלום”) | batch לא נותן זאת. הרכיבו פיצוי בקוד שלכם |
| טעינה של אלפי רשומות שאסור שתפעיל אוטומציות | כיבוי זמני של הטריגרים בתיאום, או חלון שקט |
| קריאה בלבד | batch הוא לכתיבות. GET בתוך batch מוחזר כשגיאה. לקריאה — שאילתות |
צ’קליסט
Section titled “צ’קליסט”- אצווה של עד 50 פעולות; חתכו בצד שלכם — השרת לא יעצור אתכם.
pathמתחיל ב‑/parse.- עברתם על כל איבר בתשובה ובדקתם
successמולerror. - שמרתם את הבקשה המקורית לצד כל כישלון, כדי שיהיה מה לנסות שוב.
- שאלתם מי מחזיק את הטריגרים של הטבלה — לפני שהרצתם.
- הוספתם השהיה קצרה בין קבוצות.
המשך מכאן
Section titled “המשך מכאן”- עבודה עם רשומות — הפעולה הבודדת שכל איבר ב‑batch חוזר עליה
- שאילתות — הצד הקורא
- מגבלות ומכסות — כל המספרים במקום אחד
- שרת ה‑MCP —
Create-Manyוהדגלים שמשתיקים אוטומציות - שגיאות ופתרון תקלות