Page through a collection using the Microsoft Graph SDKs
Article
For performance reasons, collections of entities are often split into pages and each page is returned with a URL to the next page. The PageIterator class simplifies consuming of paged collections. PageIterator handles enumerating the current page and requesting subsequent pages automatically.
If you send any additional request headers in your initial request, those headers are not included by default in subsequent page requests. If those headers need to be sent on subsequent requests, you must set them explicitly.
Iterate over all the messages
The following example shows iterating over all the messages in a user's mailbox.
Tip
This example sets a small page size using the top parameter for demonstration purposes. You can set the page size up to 999 to minimize the number of requests that are necessary.
headers := abstractions.NewRequestHeaders()
headers.Add("Prefer", "outlook.body-content-type=\"text\"")
var pageSize int32 = 10
query := users.ItemMessagesRequestBuilderGetQueryParameters{
Select: []string{"body", "sender", "subject"},
Top: &pageSize,
}
options := users.ItemMessagesRequestBuilderGetRequestConfiguration{
Headers: headers,
QueryParameters: &query,
}
result, err := graphClient.Me().Messages().Get(context.Background(), &options)
if err != nil {
log.Fatalf("Error getting messages: %v\n", err)
}
// Initialize iterator
pageIterator, err := graphcore.NewPageIterator[*models.Message](
result,
graphClient.GetAdapter(),
models.CreateMessageCollectionResponseFromDiscriminatorValue)
if err != nil {
log.Fatalf("Error creating page iterator: %v\n", err)
}
// Any custom headers sent in original request should also be added// to the iterator
pageIterator.SetHeaders(headers)
// Iterate over all pages
err = pageIterator.Iterate(
context.Background(),
func(message *models.Message)bool {
fmt.Printf("%s\n", *message.GetSubject())
// Return true to continue the iterationreturntrue
})
if err != nil {
log.Fatalf("Error iterating over messages: %v\n", err)
}
Java
ArrayList<Message> messages = new ArrayList<>();
MessageCollectionResponse messageResponse = graphClient.me().messages().get( requestConfiguration -> {
requestConfiguration.headers.add("Prefer", "outlook.body-content-type=\"text\"");
requestConfiguration.queryParameters.select = new String[] {"sender, subject, body"};
requestConfiguration.queryParameters.top = 10;
});
PageIterator<Message, MessageCollectionResponse> pageIterator =
new PageIterator.Builder<Message, MessageCollectionResponse>()
.client(graphClient)
// Response from the first request
.collectionPage(Objects.requireNonNull(messageResponse))
// Factory to create a new collection response
.collectionPageFactory(MessageCollectionResponse::createFromDiscriminatorValue)
// Used to configure subsequent requests
.requestConfigurator( requestInfo -> {
// Re-add the header and query parameters to subsequent requests
requestInfo.headers.add("Prefer", "outlook.body-content-type=\"text\"");
requestInfo.addQueryParameter("%24select", new String[] {"sender, subject, body"});
requestInfo.addQueryParameter("%24top", 10);
return requestInfo;
})
// Callback executed for each item in the collection
.processPageItemCallback( message -> {
messages.add(message);
returntrue;
}).build();
pageIterator.iterate();
PHP
$query = new MessagesRequestBuilderGetQueryParameters(
top: 10,
select: ['sender', 'subject', 'body']);
$config = new MessagesRequestBuilderGetRequestConfiguration(
queryParameters: $query,
headers: ['Prefer' => 'outlook.body-content-type="text"']);
$messages = $graphClient->me()
->messages()
->get($config)
->wait();
// Microsoft\Graph\Core\Tasks\PageIterator
$pageIterator = new PageIterator($messages, $graphClient->getRequestAdapter());
$callback = function($message): bool{
/** @var Models\Message $message */print($message->getSubject().PHP_EOL);
// Return true to continue iterationreturntrue;
};
// Re-add the header to subsequent requests
$pageIterator->setHeaders(['Prefer' => 'outlook.body-content-type="text"']);
$pageIterator->iterate($callback);
TypeScript
const response: PageCollection = await graphClient
.api('/me/messages?$top=10&$select=sender,subject,body')
.header('Prefer', 'outlook.body-content-type="text"')
.get();
// A callback function to be called for every item in the collection.// This call back should return boolean indicating whether not to// continue the iteration process.const callback: PageIteratorCallback = (message: Message) => {
console.log(message.subject);
returntrue;
};
// A set of request options to be applied to// all subsequent page requestsconst requestOptions: GraphRequestOptions = {
// Re-add the header to subsequent requests
headers: {
Prefer: 'outlook.body-content-type="text"',
},
};
// Creating a new page iterator instance with client a graph client// instance, page collection response from request and callbackconst pageIterator = new PageIterator(
graphClient,
response,
callback,
requestOptions,
);
// This iterates the collection until the nextLink is drained out.await pageIterator.iterate();
Stopping and resuming the iteration
Some scenarios require stopping the iteration process in order to perform other actions. It is possible to pause the iteration by returning false from the iteration callback. Iteration can be resumed by calling the resume method on the PageIterator.
var messages = await graphClient.Me.Messages
.GetAsync(requestConfiguration =>
{
requestConfiguration.QueryParameters.Top = 10;
});
while (messages?.Value != null)
{
foreach (var message in messages.Value)
{
Console.WriteLine(message.Subject);
}
// If OdataNextLink has a value, there is another pageif (!string.IsNullOrEmpty(messages.OdataNextLink))
{
// Pass the OdataNextLink to the WithUrl method// to request the next page
messages = await graphClient.Me.Messages
.WithUrl(messages.OdataNextLink)
.GetAsync();
}
else
{
// No more results, exit loopbreak;
}
}
Learn how to show a user's emails in ASP.NET Core apps with Microsoft Graph. Also, learn how to optimize Microsoft Graph queries and load email in batches.