0
# Batch Processing
1
2
Transaction-based batch operations allowing multiple requests to be sent together with rollback capabilities. Batches can contain GET requests directly and change sets for transactional operations, providing efficient bulk processing with automatic error handling.
3
4
## Capabilities
5
6
### Batch Class
7
8
Container for multiple requests that can be processed as a single operation.
9
10
```typescript { .api }
11
/**
12
* Batch request container for sending multiple operations together
13
*/
14
class Batch implements BatchParameters {
15
/** Batch identifier name */
16
name?: string;
17
/** Change sets containing transactional operations */
18
changeSets?: Array<ChangeSet>;
19
/** GET requests (cannot be in change sets) */
20
requests?: Array<BatchRequest>;
21
/** Headers to apply to the entire batch */
22
headers?: Array<Header>;
23
/** Override async setting for batch */
24
async?: boolean;
25
/** Internal flag for URL length handling */
26
isOverLengthGet?: boolean;
27
28
constructor(parameters: BatchParameters);
29
}
30
31
interface BatchParameters extends BaseParameters {
32
name?: string;
33
changeSets?: Array<ChangeSet>;
34
requests?: Array<BatchRequest>;
35
isOverLengthGet?: boolean;
36
}
37
```
38
39
### ChangeSet Class
40
41
Transactional container where all operations succeed or fail together.
42
43
```typescript { .api }
44
/**
45
* Transactional container for batch operations - all requests succeed or fail together
46
*/
47
class ChangeSet implements ChangeSetParameters {
48
/** Change set identifier name */
49
name?: string;
50
/** Array of batch requests in this change set */
51
requests?: Array<BatchRequest>;
52
53
constructor(parameters: ChangeSetParameters);
54
}
55
56
interface ChangeSetParameters {
57
name?: string;
58
requests?: Array<BatchRequest>;
59
}
60
```
61
62
### BatchRequest Class
63
64
Individual request within a batch or change set.
65
66
```typescript { .api }
67
/**
68
* Individual request that can be included in batches or change sets
69
*/
70
class BatchRequest implements BatchRequestParameters {
71
/** HTTP method (GET, POST, PATCH, DELETE) */
72
method: string;
73
/** Request URL (optional, can be auto-generated) */
74
url?: string;
75
/** Request payload as JSON string */
76
payload?: string;
77
/** Headers specific to this request */
78
headers?: Array<Header>;
79
/** Content ID for referencing in other requests */
80
contentId?: string;
81
82
constructor(params: BatchRequestParameters);
83
}
84
85
interface BatchRequestParameters {
86
method: string;
87
url?: string;
88
payload?: string;
89
headers?: Array<Header>;
90
contentId?: string;
91
}
92
```
93
94
### Send Batch Operation
95
96
Executes a batch request and returns comprehensive response information.
97
98
```typescript { .api }
99
/**
100
* Send a batch request to CRM
101
* @param batch - Batch object containing requests and change sets
102
* @returns Promise resolving to batch response with results and error information
103
*/
104
function SendBatch(batch: Batch): Promise<BatchResponse> | BatchResponse;
105
```
106
107
### Batch Response Classes
108
109
```typescript { .api }
110
/**
111
* Response from a batch operation containing all individual responses
112
*/
113
class BatchResponse implements BatchResponseParameters {
114
/** Batch identifier name */
115
name?: string;
116
/** Responses from change sets */
117
changeSetResponses?: Array<ChangeSetResponse>;
118
/** Responses from direct batch requests (GET operations) */
119
batchResponses?: Array<Response>;
120
/** Whether any requests in the batch failed */
121
isFaulted?: boolean;
122
/** Collection of all error responses */
123
errors?: Array<string>;
124
/** Original XMLHttpRequest object */
125
xhr?: XMLHttpRequest;
126
127
constructor(parameters: BatchResponseParameters);
128
}
129
130
interface BatchResponseParameters {
131
name?: string;
132
changeSetResponses?: Array<ChangeSetResponse>;
133
batchResponses?: Array<Response>;
134
isFaulted?: boolean;
135
errors?: Array<string>;
136
xhr?: XMLHttpRequest;
137
}
138
139
/**
140
* Response from a change set containing individual request responses
141
*/
142
class ChangeSetResponse {
143
/** Change set identifier name */
144
name?: string;
145
/** Individual responses for each request in the change set */
146
responses?: Array<Response>;
147
}
148
149
/**
150
* Individual response from a batch request
151
*/
152
class Response implements ResponseParameters {
153
/** Raw response data */
154
rawData?: string;
155
/** Content ID if specified in request */
156
contentId?: string;
157
/** Parsed response payload */
158
payload?: object;
159
/** HTTP status code */
160
status?: string;
161
/** Response headers as key-value object */
162
headers?: any;
163
164
constructor(parameters: ResponseParameters);
165
}
166
167
interface ResponseParameters {
168
rawData?: string;
169
contentId?: string;
170
payload?: object;
171
status?: string;
172
headers?: any;
173
}
174
```
175
176
## Usage Examples
177
178
### Basic Batch with Change Set
179
180
```typescript
181
// Create multiple related records in a transaction
182
const batch = new WebApiClient.Batch({
183
changeSets: [
184
new WebApiClient.ChangeSet({
185
requests: [
186
WebApiClient.Create({
187
entityName: "account",
188
entity: { name: "Parent Account" },
189
headers: [{ key: "Prefer", value: "return=representation" }],
190
asBatch: true
191
}),
192
WebApiClient.Create({
193
entityName: "contact",
194
entity: {
195
firstname: "John",
196
lastname: "Doe",
197
"parentcustomerid_account@odata.bind": "/accounts($1)" // Reference to first request
198
},
199
asBatch: true
200
})
201
]
202
})
203
]
204
});
205
206
const batchResponse = await WebApiClient.SendBatch(batch);
207
208
if (batchResponse.isFaulted) {
209
console.log("Batch failed:", batchResponse.errors);
210
} else {
211
console.log("All operations succeeded");
212
console.log(batchResponse.changeSetResponses[0].responses);
213
}
214
```
215
216
### Mixed Batch with GET and Change Operations
217
218
```typescript
219
// Combine read operations with transactional writes
220
const batch = new WebApiClient.Batch({
221
// GET requests go directly in batch (not in change sets)
222
requests: [
223
WebApiClient.Retrieve({
224
entityName: "systemuser",
225
queryParams: "?$select=fullname,systemuserid&$filter=isdisabled eq false",
226
asBatch: true
227
})
228
],
229
// Change operations go in change sets for transactions
230
changeSets: [
231
new WebApiClient.ChangeSet({
232
requests: [
233
WebApiClient.Update({
234
entityName: "account",
235
entityId: "12345678-1234-1234-1234-123456789abc",
236
entity: { name: "Updated Account" },
237
asBatch: true
238
}),
239
WebApiClient.Delete({
240
entityName: "contact",
241
entityId: "87654321-4321-4321-4321-cba987654321",
242
asBatch: true
243
})
244
]
245
})
246
]
247
});
248
249
const result = await WebApiClient.SendBatch(batch);
250
251
// Access GET request results
252
const userResults = result.batchResponses[0].payload;
253
254
// Access change set results
255
const changeResults = result.changeSetResponses[0].responses;
256
```
257
258
### Batch with Actions and Functions
259
260
```typescript
261
// Execute multiple CRM actions in a transaction
262
const batch = new WebApiClient.Batch({
263
changeSets: [
264
new WebApiClient.ChangeSet({
265
requests: [
266
WebApiClient.Execute(
267
WebApiClient.Requests.QualifyLeadRequest.with({
268
entityId: "lead-guid-here",
269
payload: {
270
CreateAccount: true,
271
CreateContact: true,
272
CreateOpportunity: true,
273
Status: 3
274
}
275
}),
276
{ asBatch: true }
277
),
278
WebApiClient.Execute(
279
WebApiClient.Requests.SendEmailRequest.with({
280
payload: {
281
TemplateId: "template-guid-here",
282
RegardingId: "opportunity-guid-here",
283
RegardingType: "opportunity"
284
}
285
}),
286
{ asBatch: true }
287
)
288
]
289
})
290
]
291
});
292
293
const actionResults = await WebApiClient.SendBatch(batch);
294
```
295
296
### Large FetchXML Automatic Batch
297
298
```typescript
299
// Long FetchXML queries are automatically converted to batch requests
300
const longFetchXml = `
301
<fetch mapping='logical'>
302
<entity name='account'>
303
<attribute name='name'/>
304
<attribute name='accountid'/>
305
<!-- Very long fetch query that exceeds URL limits -->
306
<filter type='and'>
307
<condition attribute='name' operator='like' value='%very long search term%'/>
308
<!-- Many more conditions... -->
309
</filter>
310
<link-entity name='contact' from='parentcustomerid' to='accountid'>
311
<!-- Many attributes and conditions -->
312
</link-entity>
313
</entity>
314
</fetch>`;
315
316
// This will automatically be sent as a batch if URL length exceeds limits
317
const results = await WebApiClient.Retrieve({
318
entityName: "account",
319
fetchXml: longFetchXml
320
});
321
```
322
323
### Error Handling and Response Processing
324
325
```typescript
326
const batch = new WebApiClient.Batch({
327
changeSets: [
328
new WebApiClient.ChangeSet({
329
name: "AccountOperations",
330
requests: [
331
WebApiClient.Create({
332
entityName: "account",
333
entity: { name: "Test Account 1" },
334
asBatch: true
335
}),
336
WebApiClient.Create({
337
entityName: "account",
338
entity: { name: "Test Account 2" },
339
asBatch: true
340
}),
341
WebApiClient.Update({
342
entityName: "account",
343
entityId: "existing-account-guid",
344
entity: { name: "Updated Name" },
345
asBatch: true
346
})
347
]
348
})
349
]
350
});
351
352
try {
353
const response = await WebApiClient.SendBatch(batch);
354
355
if (response.isFaulted) {
356
console.log("Batch contained errors:");
357
response.errors.forEach((error, index) => {
358
console.log(`Error ${index + 1}:`, error);
359
});
360
361
// Check individual change set responses
362
response.changeSetResponses.forEach((changeSetResp, csIndex) => {
363
console.log(`Change set ${csIndex} (${changeSetResp.name}):`);
364
changeSetResp.responses.forEach((resp, reqIndex) => {
365
if (resp.status !== "200" && resp.status !== "201") {
366
console.log(` Request ${reqIndex} failed:`, resp.status, resp.payload);
367
} else {
368
console.log(` Request ${reqIndex} succeeded:`, resp.payload);
369
}
370
});
371
});
372
} else {
373
console.log("All batch operations succeeded");
374
375
// Process successful results
376
const changeSetResults = response.changeSetResponses[0].responses;
377
changeSetResults.forEach((result, index) => {
378
console.log(`Operation ${index} result:`, {
379
status: result.status,
380
entityId: result.headers["OData-EntityId"],
381
data: result.payload
382
});
383
});
384
}
385
} catch (networkError) {
386
console.error("Network or request error:", networkError);
387
}
388
```
389
390
### Content ID References
391
392
```typescript
393
// Use content IDs to reference results from previous requests in the same batch
394
const batch = new WebApiClient.Batch({
395
changeSets: [
396
new WebApiClient.ChangeSet({
397
requests: [
398
// Create account with content ID
399
new WebApiClient.BatchRequest({
400
method: "POST",
401
url: WebApiClient.GetApiUrl() + WebApiClient.GetSetName("account"),
402
payload: JSON.stringify({ name: "Parent Account" }),
403
headers: [
404
{ key: "Content-Type", value: "application/json" },
405
{ key: "Prefer", value: "return=representation" }
406
],
407
contentId: "1"
408
}),
409
// Create contact referencing the account
410
new WebApiClient.BatchRequest({
411
method: "POST",
412
url: WebApiClient.GetApiUrl() + WebApiClient.GetSetName("contact"),
413
payload: JSON.stringify({
414
firstname: "John",
415
lastname: "Doe",
416
"parentcustomerid_account@odata.bind": "$1" // References content ID 1
417
}),
418
headers: [
419
{ key: "Content-Type", value: "application/json" }
420
],
421
contentId: "2"
422
})
423
]
424
})
425
]
426
});
427
428
const result = await WebApiClient.SendBatch(batch);
429
```
430
431
## Best Practices
432
433
1. **Use Change Sets for Transactional Operations**: Put related create, update, delete operations in change sets to ensure data consistency.
434
435
2. **Keep GET Requests Outside Change Sets**: Retrieve operations must be placed directly in the batch requests array.
436
437
3. **Limit Batch Size**: Keep batches under 100 requests for optimal performance.
438
439
4. **Handle Errors Properly**: Always check the `isFaulted` property and process errors appropriately.
440
441
5. **Use Content IDs for Dependencies**: When requests depend on results from previous requests in the same batch, use content IDs for references.
442
443
6. **Set Appropriate Headers**: Include necessary headers like `Prefer: return=representation` when you need response data.
444
445
7. **Monitor Performance**: Large batches may take longer to process and could timeout in some scenarios.