The moment your web part needs more than SharePoint data — a user's manager, their calendar, Teams membership, profile photos — you're talking to Microsoft Graph. The beautiful part of SPFx is that you never handle a token yourself. The framework brokers authentication through a shared Azure AD application, and you just ask for a client. But that convenience hides an approval flow every developer must understand.
The two HTTP clients
SPFx gives you two purpose-built, pre-authenticated clients via this.context:
- MSGraphClient — a wrapper around the Microsoft Graph JavaScript SDK, with a fluent
.api()builder. Use this for Graph calls. - AadHttpClient — a generic client for any Azure AD-secured API (Graph or your own custom API). You pass the resource/audience and it attaches the right bearer token.
Calling Graph with MSGraphClient
Acquire the client from the factory, then build the request. Here we read the signed-in user's profile and manager:
import { MSGraphClientV3 } from '@microsoft/sp-http';
private async loadProfile(): Promise<void> {
const client: MSGraphClientV3 =
await this.context.msGraphClientFactory.getClient('3');
const me = await client.api('/me')
.select('displayName,jobTitle,mail')
.get();
const manager = await client.api('/me/manager')
.select('displayName,mail')
.get();
console.log(me.displayName, '→ reports to →', manager.displayName);
}
$select, $filter, $expand, $top — exposed as .select(), .filter(), etc. Always .select() the exact fields you need; Graph endpoints often return large default property sets otherwise.
Declaring permission requests
SPFx web parts get their tokens from a single shared Azure AD application in your tenant (the "SharePoint Online Client Extensibility" service principal). By default it can do very little. To call /me/manager you must declare the permission your solution needs in config/package-solution.json:
{
"solution": {
"name": "contoso-graph-webpart",
"id": "…",
"webApiPermissionRequests": [
{ "resource": "Microsoft Graph", "scope": "User.Read.All" }
]
}
}
This does not grant the permission — it requests it. When you deploy the .sppkg, a tenant administrator must approve the request in the SharePoint admin center under Advanced → API access (or via PnP PowerShell Approve-PnPTenantServicePrincipalPermissionRequest). Until then, your Graph call fails with a token error.
User.Read.All over Directory.Read.All — because you're widening the blast radius for the whole tenant, not just your web part.
Calling a custom API with AadHttpClient
For your own Azure-hosted API secured by Azure AD, use AadHttpClient. You pass your API's application ID URI as the resource, and SPFx fetches a token scoped to it:
import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http';
private async callCustomApi(): Promise<void> {
const client: AadHttpClient =
await this.context.aadHttpClientFactory
.getClient('api://contoso-orders-api'); // your AAD app's URI
const res: HttpClientResponse = await client.get(
'https://contoso-orders.azurewebsites.net/api/orders',
AadHttpClient.configurations.v1);
const orders = await res.json();
}
The matching permission request names your API instead of Graph:
"webApiPermissionRequests": [
{ "resource": "contoso-orders-api", "scope": "user_impersonation" }
]
Application vs delegated permissions
One crucial limitation: SPFx clients always run as the signed-in user (delegated permissions). There is no app-only token from SPFx. If an operation needs application permissions — acting without a user, or accessing data the user shouldn't see directly — you must front it with your own API (an Azure Function) that holds the app credential, and call that from SPFx with AadHttpClient. I cover building exactly that pattern in a later article on securing custom APIs.
Practical checklist
- Declare every scope in
webApiPermissionRequestsbefore you call. - Have an admin approve them in API access — and document which scopes your solution needs so reviewers can reason about them.
- Request least privilege; permissions are tenant-wide and shared.
- Use
MSGraphClientV3for Graph,AadHttpClientfor custom APIs. - For app-only scenarios, broker through your own AAD-secured API.
Once you're comfortable acquiring tokens through these clients, the entire Microsoft 365 surface opens up to your web parts — users, groups, Teams, mail, calendars, files, and your own line-of-business APIs — all without ever touching a client secret in browser code.