Easily debug Microsoft Graph Python SDK requests


Using the Microsoft Graph Python SDK is a convenient way to connect your app to Microsoft 365. Here’s how you can easily debug API requests issued from the SDK.

Build apps that work with Microsoft 365

Microsoft 365 is a platform where millions of users work together every day. You can create apps that use the data and insights from Microsoft 365 to help them do their work more efficiently and effectively.

Microsoft Graph is the API that connects you to your organization’s data in Microsoft 365. You can access it using its REST endpoints. If you build an app connected to Microsoft 365 using Python, you should consider using the Microsoft Graph Python SDK. The SDK makes authentication easier and handles complex scenarios like backing off when throttled, following redirects and uploading large files, which means that you can focus on building your app instead of its plumbing!

Use the Microsoft Graph Python SDK to connect your Python app to Microsoft 365

To begin using the Microsoft Graph Python SDK, create a Graph client with an authentication provider. Then, you can start calling Microsoft Graph to access and manipulate data in Microsoft 365.

For building a deamon app, you’d use code similar to following:

from azure.identity import ClientSecretCredential
from msgraph import GraphServiceClient

_credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret)
_scopes = ['https://graph.microsoft.com/.default']

graph_client = GraphServiceClient(_credential, _scopes)

Debug Microsoft Graph Python SDK API requests

When you start building your app, it can happen that you’ll get an error calling Microsoft Graph. Looking at the returned error information should give you a clearer idea of what’s wrong and how to fix it.

If that’s not the case though, and you need to see the request and response that goes over the wire, here’s a trick that you can use.

Microsoft Graph Python SDK middleware

The Microsoft Graph Python SDK supports the concept of middleware: functions that run as part of its request- and response pipeline and have access to the information about the outgoing request and received response. If you want to inspect API requests and responses issued by the Microsoft Graph Python SDK, building a custom middleware is the easiest way about it. Middleware has access to the full request context information which includes information about the outgoing request and received response.

Debug middleware for Microsoft Graph Python SDK

Start, with creating a new file named debug_handler.py. Add the following code:

from kiota_http.middleware import BaseMiddleware
import httpx

class DebugHandler(BaseMiddleware):

    async def send(
        self, request: httpx.Request, transport: httpx.AsyncBaseTransport
    ) -> httpx.Response:
        print()
        print(f"{request.method} {request.url}")
        for key, value in request.headers.items():
            print(f"{key}: {value}")
        if request.content:
            print()
            print("Request body:")
            print(request.content.decode())

        response: httpx.Response = await super().send(request, transport)

        print()
        print(f"Response: {response.status_code} {response.reason_phrase}")
        print("Response headers:")
        for key, value in response.headers.items():
            print(f"{key}: {value}")

        print()
        print("Response body:")
        response_content = await response.aread()
        print(f"Response content: {response_content.decode()}")

        return response

You start with defining a new handler that inherits from the BaseMiddleware class which defines middleware handlers for the Microsoft Graph Python SDK. Next, you implement the send method, that is called on each API request handled by the SDK.

When this function runs as part of the client’s middleware, it will first log information about the outgoing request. Then, it will call the next middleware in chain and wait for its execution. This is where the actual API request will be executed. Finally, it will print response result, its headers, and if suitable, the response body.

Use the debug middleware

The last step is to add the debug middleware to the Graph client. To do that, update the client’s initiation code as follows:

from azure.identity import ClientSecretCredential
from kiota_authentication_azure.azure_identity_authentication_provider import (
    AzureIdentityAuthenticationProvider
)
from msgraph import GraphServiceClient, GraphRequestAdapter
from msgraph_core import GraphClientFactory
from debug_handler import DebugHandler

_credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret)
_auth_provider = AzureIdentityAuthenticationProvider(_credential)

_middleware = GraphClientFactory.get_default_middleware(None)
_middleware.append(DebugHandler())
_http_client = GraphClientFactory.create_with_custom_middleware(
    _middleware
)
_adapter = GraphRequestAdapter(_auth_provider, _http_client)

graph_client = GraphServiceClient(
    _credential,
    scopes=["https://graph.microsoft.com/.default"],
    request_adapter=_adapter,
)

We changed the Graph client instantiation code, to get the default middleware chain, which includes authentication, handling throttling and ends with issuing the web request. For debugging to have access to all request and response information, we need to add it to the end of the middleware collection.

With this setup in place, when you call Microsoft Graph using the client, you’ll see the following output in the console:

TIP: To see this middleware in action, check out the sample Microsoft Graph connector.

$ python3 main.py

GET https://graph.microsoft.com/v1.0/users?$top=1
host: graph.microsoft.com
accept-encoding: gzip, deflate
connection: keep-alive
accept: application/json
authorization: Bearer eyJ0eXAiOiJKV1QiL...
user-agent: python-httpx/0.25.0 kiota-python/0.6.1

Response: 200 OK
Response headers:
cache-control: no-cache
transfer-encoding: chunked
content-type: application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
content-encoding: gzip
vary: Accept-Encoding
strict-transport-security: max-age=31536000
request-id: f7711087-2274-4077-9c47-c8909c18f7ba
client-request-id: f7711087-2274-4077-9c47-c8909c18f7ba
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West Europe","Slice":"E","Ring":"5","ScaleUnit":"004","RoleInstance":"AM2PEPF0001D77E"}}
x-ms-resource-unit: 1
odata-version: 4.0
pubDate: Tue, 24 Oct 2023 09:37:10 GMT

Response body:
Response content: {"@odata.context":"https://graph.microsoft.com/v1.0/$metadata#users","@odata.nextLink":"https://graph.microsoft.com/v1.0/users?$top=1&$skiptoken=RFNwdAIAAQAAAB46QWRlbGVWQDJjemszZy5vbm1pY3Jvc29mdC5jb20pVXNlcl82ZGU4ZWMwNC02Mzc2LTQ5MzktYWI0Ny04M2EyYzg1YWI1ZjW5AAAAAAAAAAAAAA","value":[{"businessPhones":["+1 425 555 0109"],"displayName":"Adele Vance","givenName":"Adele","jobTitle":"Retail Manager","mail":"AdeleV@2czk3g.onmicrosoft.com","mobilePhone":null,"officeLocation":"18/2111","preferredLanguage":"en-US","surname":"Vance","userPrincipalName":"AdeleV@2czk3g.onmicrosoft.com","id":"6de8ec04-6376-4939-ab47-83a2c85ab5f5"}]}

Summary

Using the Microsoft Graph Python SDK is an easy way for you to get the data and insights from Microsoft 365 in your app. The SDK simplifies authentication and handles complex API scenarios for you. If you ever need to debug the requests and responses handled by the SDK, you can build a simple debug middleware to log the raw requests and responses for you.

Others found also helpful: