Identify which request failed in Microsoft Graph Batch request
Tags: microsoftgraph, batch
Published: 2022-10-03 05:59:19

Microsoft Graph provides **Batch** request feature which saves network round trips to improve performance. 

We can include different types of requests, such as ``GET/POST/DELETE`` or resources such as ``Users/Groups/Events``, etc.

Even though this is useful, it could be difficult to identify the reason why some of the request has been failed depending on the request type.

## Example Scenario

If we request batch to remove users from a group, for example, it will fail if the user is not part of the group. However, the error message does not give us information about which user id was failed to remove.

This is the error when the request fails.

```json
"error": {
    "code": "Request_ResourceNotFound",
    "message": "Resource 'be24f653-c890-454c-a465-0297c3de2fc2' does not exist or one of its queried reference-property objects are not present.",
    "innerError": {
        "date": "2022-10-03T04:06:29",
        "request-id": "af2627a2-428d-4061-bb3a-00247e8589cc",
        "client-request-id": "e03aab35-0657-c9e2-6703-c38debae955c"
    }
}
```

In such case, we need to know actual request itself to know user id.

## Unique Request Id in batch

We need to add ``BatchRequestStep`` list to batch request, and each ``BatchRequestStep`` has ``RequestId`` property. This id will be part of response.

## Sample code

Following sample explain how to check response as well as request. I only added two requests for simplify the code, but we can add more. We can set any unique request id, so I used ``user id`` here, which I use to show in console as ``user id``.

The ``Key`` of batch response is corresponding to request id.

```csharp
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.Net;
using System.Net.Http.Headers;

string tenantId = "tenantId";
string clientId = "clientId";
string clientSecret = "clientSecret";
string baseUrl = "https://graph.microsoft.com/v1.0";
IConfidentialClientApplication app = 
    ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)
    .WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
    .Build();

string[] scopes = new[] { "https://graph.microsoft.com/.default" };

AuthenticationResult authenticationResult = 
    await app.AcquireTokenForClient(scopes).ExecuteAsync();

GraphServiceClient graphClient = new (
    new DelegateAuthenticationProvider(r => {
        r.Headers.Authorization =
            new AuthenticationHeaderValue(
                "bearer", authenticationResult.AccessToken);
        return Task.FromResult(0);
    }));

List<User> users = new();
BatchRequestContent batchRequestContent = new();
string groupId = "the groupId";
string memberId1 = "the memberId1";
batchRequestContent.AddBatchRequestStep(
    new BatchRequestStep(
        memberId1,
        new HttpRequestMessage(
            HttpMethod.Delete,
            $"{baseUrl}/groups/{groupId}/members/{memberId1}/$ref")
        )
    );
string memberId2 = "the memberId2";
batchRequestContent.AddBatchRequestStep(
    new BatchRequestStep(
        memberId2,
        new HttpRequestMessage(
            HttpMethod.Delete,
            $"{baseUrl}/groups/{groupId}/members/{memberId2}/$ref")
        )
    );

BatchResponseContent batchResponseContent = 
    await graphClient.Batch.Request().PostAsync(batchRequestContent);

foreach(KeyValuePair<string, HttpResponseMessage> response 
    in await batchResponseContent.GetResponsesAsync())
{
    if(response.Value.StatusCode != HttpStatusCode.OK)
    {
        BatchRequestStep requestStep = batchRequestContent.BatchRequestSteps[response.Key];
        Console.WriteLine($"StatusCode: {response.Value.StatusCode}");
        Console.WriteLine($"Request URI: {requestStep.Request.RequestUri}");
        Console.WriteLine($"Failed user: {requestStep.RequestId}");
    }
}
```

## Summary

Batch is really useful feature for Microsoft Graph, so please try to use it as much as possible. For unit testing the batch request, I have another post [here](https://dev.to/kenakamu/c-how-to-unit-test-graph-sdk-batch-request-38j2)