Graph API integration for SaaS developers
Published Jan 26 2024 07:27 AM 3,146 Views
Microsoft

Many applications integrate with Microsoft Graph APIs to enrich their functionality with user data or provide additional features on top of Office 365 or Azure services.

Some best practices can be leveraged to efficiently consume data from Microsoft Graph. This sample shows how to use delta tokens to retrieve a change set, in combination with subscriptions/webhooks, to get notified when a delta token should be used to get the latest changes without pulling the whole set of data each time. 

 

Understanding Graph API throttling

 

Throttling limits prevent excessive use that could potentially impact service availability. Most services apply limits to ensure optimal performance and reliability. Microsoft Graph API applies a multilayered throttling approach with service-specific limits

Common reasons for throttling include:

  • High Request Volume: Exceeding the rate limit by sending numerous requests in a short time.
  • Resource-Intensive Queries: Throttling may occur with complex or large data requests.
  • Tenant and Service-Level Limits: Throttling can affect both individual tenants and the overall service, especially in multi-tenant environments like Entra applications.
  • Service-wide Demand: Increased demand on Microsoft Graph can result in service-wide throttling.

 

Handling limitations

Handling limitations is crucial. Throttling applies to service principals or Enterprise Applications, automatically created during App Registration in the Azure portal or manually using Azure CLI/Graph API. The universal limitation for all applications is 130,000 requests per 10 seconds per app across all tenants. Services may have distinct limitations, such as combined limits for app registrations or specific limits for individual apps in separate tenants.

Throttling is inevitable due to various limitations at different levels. Requests vary in price, with read operations being less demanding than writes.
Using OData correctly to query Microsoft Graph, can significantly reduce amount of requests and traffic. Careful use of the $expand query operator is essential, as it can significantly increase data transfer. The $expand operator fetches related data, reducing the number of requests. In combination $select, $top, or $skip can be used to project data and/or paginate

Batching is also a way to reduce the number of operations per request: instead of sending multiple requests for each operation, you can combine them into a $batch request to reduce the number of requests sent.

 

Limiting data fields in results

Microsoft Graph responses include default properties when no result set is specified. If you only need specific properties like display name and email address, using the $select parameter with a comma-delimited list optimizes the request and speeds up the response:

 

https://graph.microsoft.com/v1.0/users?$select=id,givenName,surname

 

Limit the number of returned results

The $top parameter enables you to limit the response to only include a specific number of records.

 

https://graph.microsoft.com/v1.0/users?$top=50

 

When it actually happens

How do you know you are throttled? It can easily be done by inspecting the HTTP status code in the response. The status code 429 indicates too many requests and is how Microsoft Graph tells the client their requests are being throttled. Graph SDK provides retry handlers, which will retry if throttled - a common pattern in most SDKs. Graph SDK is available for most of the popular programming languages including C#, Java, Python, JS, PowerShell, Go, and PHP. In our sample, we utilized Graph SDK to take advantage of built-in capabilities for retries and exponential back-off. Here is an example of specifying retry policy:

 

var retryHandlerOption = new RetryHandlerOption
{
    MaxRetry = 7,
    ShouldRetry = (delay,attempt,message) => true
};
var user = await graphClient.Me.GetAsync(requestConfiguration => requestConfiguration.Options.Add(retryHandlerOption));

 

However, if there is a need to interact with Graph API directly, you have to implement inspection of the HTTP header Retry-After, logic for retries, and exponential backoff logic yourself.

 

Graph API shadowing sample for users & groups

 

Now that you gained a better understanding of what can be done to avoid throttling, it's a good time to have a look at the sample we have created to showcase how to implement best practices for dealing with throttling.

In this sample, we synchronize users and groups from Graph API into our SaaS app local storage (Cosmos DB in our case). Users and groups are synchronized from customers' tenants (Azure AD) into SaaS solution. 

 

Overall architecture

In our sample, Azure Durable Functions to persist delta tokens and subscriptions for each tenant. Cosmos DB persists the retrieved data, using a partitioning key composed of tenant ID (the customer tenant) and OData type (user or group in this sample).

 

irina_kostina_0-1706094269685.png

 

This sample follows the main design principles when working with Graph, such as utilizing delta queries for getting change sets/deltas, as well as utilizing subscription webhooks to provide reactive functionality for scheduling the use of the delta token.

Although this sample is for user and group objects, it can be applied to many other Graph API resources.

In summary, the main principle is, to use OData delta tokens in conjunction with subscriptions (webhooks) notifications. When a notification arrives for a tenant, it's registered for 'later' processing. A scheduled background job for the registered tenants then runs using the delta tokens. This approach reduces the calls to Graph, only using delta tokens, and not calling Graph on every subscription triggered.

Let’s understand the principles of delta queries and change notifications in more detail.

 

Delta queries running on schedule

In our scenario, the application is polling Microsoft Graph at scheduled intervals, such as 6 or 12 hours. Typically this strategy is used to keep the application’s local data store in sync with data exposed in Microsoft Graph. 

One way to mitigate throttling when polling Microsoft Graph for large data sets is to use the delta query feature of Microsoft Graph. Delta query, also known as "change tracking" allows developers to request only data that has been added, updated, or deleted since the last request. This pattern will allow the application to reduce the amount of data requested by the application will reduce the cost of the request and as such, likely limit the chances of the requests being throttled.

Delta query works using the concept of a state token. An application will issue an initial request to Microsoft Graph the same way it normally does, except it will include the delta link function in the request.

Microsoft Graph will respond with the requested data as normal, except the last page of data will include an extra property deltaLink. This contains the endpoint with the state token of the delta query. 

To request the changes that happened in the resulting data set from the time the previous query was submitted, the application uses the endpoint from the previous request’s deltaLink endpoint. This tells Microsoft Graph to execute the same query, but only return the items that have changed since the first request, as indicated using the state token included in the deltaLink property. The last page of results on this second request will include a new deltaLink property value that can be used for the next request, and so on.

You can find here a more detailed explanation of working with deltaLinks: Get incremental changes for users - Microsoft Graph | Microsoft Learn .

 

Combine delta query with change notifications

An application can subscribe to be notified when a specific resource changes, such as the users. In this case, when a user is added, updated, or deleted, the application is notified of something changed in the users endpoint by Microsoft Graph via an HTTP POST. The application can then use the delta query to request all changes since the last time it made the request.

Keep in mind, that to receive notifications, you have to create a subscription for the resource (users and groups in this case). During this process of creating a subscription, you need to submit the webhook URL as well as the expiry date and have a process of prolonging the expiry date of the subscription. 

Using this strategy, applications can nearly eliminate the need to frequently poll Microsoft Graph and process those changes to keep a local data store in sync, greatly reducing the chances for their requests to be throttled.

 

Recommendation: schedule one Delta Query request on a long interval

Webhooks do not have guaranteed delivery. To ensure no changes are missed, it's recommended to schedule at least one long-interval delta query request and not rely entirely on the subscription notifications to request changes from the Microsoft Graph resource. This will return all changed data that can be processed by the application in case a change notification is missed.

 

Conclusion

 

Navigating the intricacies of Microsoft Graph services requires a thoughtful approach to limitations and optimizations. Understanding the nuances of throttling, selecting relevant data through parameters like $select, implementing effective retry strategies, and utilizing delta queries in combination with change notifications contribute to a smoother experience. As you delve into the realm of Microsoft Graph, keeping these considerations in mind will not only enhance the efficiency of your applications but also ensure a more robust and resilient interaction with the diverse functionalities provided by this powerful platform. Make sure to check out the sample for a deeper understanding of how to implement the main design principles to deal with Graph API throttling: Azure-Samples/MicrosoftGraphShadow

 

Credits

 

Primary authors

Henrik Westergaard Hansen, Principal Customer Engineer Henrik Westergaard Hansen | LinkedIn 

Kostina Irina, Customer Engineer Irina Kostina | LinkedIn

Reviewers

Chat GPT 3.5 ChatGPT (openai.com) 

 

1 Comment
Version history
Last update:
‎Jan 26 2024 07:26 AM
Updated by: