Introduction

When working with Microsoft Graph API, you often need to retrieve large datasets that exceed the default page size limit.

In these cases, pagination (or paging) allows you to handle large responses by breaking them into manageable chunks and iterating over each page until all data is retrieved.

How Pagination works

In client-side paging, a client app specifies the number of results it wants Microsoft Graph to return in a single page by using the $top, $skip, or $skipToken query parameters.

For example, the /users endpoint supports $top but not $skip.

In server-side paging, the Microsoft Graph service returns a default number of results in a single page without the client specifying the number of results to return using $top.

For example, the GET /users endpoint returns a default of 100 results in a single page.

Key Concepts in Microsoft Graph Pagination

Prerequisites

npm install @azure/msal-browser

npm install @pnp/sp @pnp/graph

Here’s a guide on implementing paging in your app, especially in the context of a SharePoint Framework (SPFx) web part.

Paging Microsoft Graph data in SPFx

Step 1. Basic Setup for an SPFx Web Part

Create or open an SPFx project where you want to retrieve and page through data (e.g., a list of users, groups, or calendar events). Ensure MSAL.js is installed and configured if you're handling authentication in SPFx.

Step 2. Create a file GraphService.ts n the src/services folder.

// GraphService.ts

import { PublicClientApplication, Configuration, AuthenticationResult } from "@azure/msal-browser";

export class GraphService {
  private msalInstance: PublicClientApplication;

  // Set up MSAL configuration
  private msalConfig: Configuration = {
    auth: {
      clientId: "11111111-1111-1111-1111-111111111111", // Replace with your Azure AD app's client ID
      authority: "https://login.microsoftonline.com/{tenant-id}", // Replace with your Azure AD tenant ID
      redirectUri: "https://your-sp-site-url/_layouts/15/workbench.aspx" // Redirect URI for SPFx testing
    }
  };

  private graphScopes: string[] = ["User.Read.All"]; // Scopes for user data

  constructor() {
    this.msalInstance = new PublicClientApplication(this.msalConfig);
  }

  // Method to acquire an access token
  public async getAccessToken(): Promise<string | null> {
    try {
      const account = this.msalInstance.getAllAccounts()[0];
      const request = { scopes: this.graphScopes, account };
      const response: AuthenticationResult = await this.msalInstance.acquireTokenSilent(request);
      return response.accessToken;
    } catch (error) {
      const response: AuthenticationResult = await this.msalInstance.acquireTokenPopup({ scopes: this.graphScopes });
      return response.accessToken;
    }
  }

  // Method to fetch paginated user data from Microsoft Graph API
  public async fetchAllUsersWithPaging(): Promise<any[]> {
    const accessToken = await this.getAccessToken();
    if (!accessToken) {
      console.error("Failed to acquire an access token.");
      return [];
    }

    let users: any[] = [];
    let nextPageUrl = "https://graph.microsoft.com/v1.0/users?$top=50"; // Set page size with $top

    // Loop to retrieve paginated data
    while (nextPageUrl) {
      const response = await fetch(nextPageUrl, {
        headers: { Authorization: `Bearer ${accessToken}` }
      });

      if (response.ok) {
        const data = await response.json();
        users = users.concat(data.value); // Append users from current page
        nextPageUrl = data["@odata.nextLink"] || null; // Set nextPageUrl for pagination
      } else {
        console.error("Failed to fetch users:", response.statusText);
        break;
      }
    }

    return users;
  }
}

export default new GraphService();

Step 3. MySpfxComponent.tsx ( your webpart .tsx file) Fetch Data with Paging in Microsoft Graph API.

import * as React from "react";
import GraphService from "../services/GraphService";

interface MySpfxComponentState {
  users: any[];
  loading: boolean;
}

class MySpfxComponent extends React.Component<{}, MySpfxComponentState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      users: [],
      loading: true
    };
  }

  // Load users on component mount
  public async componentDidMount(): Promise<void> {
    try {
      const users = await GraphService.fetchAllUsersWithPaging();
      this.setState({ users, loading: false });
    } catch (error) {
      console.error("Error fetching users:", error);
      this.setState({ loading: false });
    }
  }

  // Render users or loading indicator
  public render(): React.ReactElement<{}> {
    const { users, loading } = this.state;

    return (
      <div>
        <h3>Microsoft Graph Users</h3>
        {loading ? (
          <p>Loading users...</p>
        ) : users.length > 0 ? (
          <ul>
            {users.map((user, index) => (
              <li key={index}>
                {user.displayName} ({user.mail})
              </li>
            ))}
          </ul>
        ) : (
          <p>No users found.</p>
        )}
      </div>
    );
  }
}

export default MySpfxComponent;

Note

Conclusion

Using Microsoft Graph API with paging enables you to manage large datasets in a controlled manner, making it ideal for applications in SPFx where you might need to display extensive user data, group lists, or calendar events. This approach keeps memory usage efficient and ensures you retrieve all necessary data without overwhelming your application.