
Msgraph Sdk
- 1 installs
- 37.5k repo stars
- Updated August 5, 2026
- github/awesome-copilot
msgraph-sdk skill documents Integrate Microsoft Graph SDK into any project - .
About
msgraph-sdk skill documents Integrate Microsoft Graph SDK into any project - .NET, TypeScript/JavaScript, or Python. Covers auth patterns (client credentials, OBO, managed identity), SDK setup, calling Graph APIs, batching, delta queries, change notifications, throttling, and permission scopes. Use when accessing Microsoft 365. name: msgraph-sdk description: 'Integrate Microsoft Graph SDK into any project - .NET, TypeScript/JavaScript, or Python. Covers auth patterns (client credentials, OBO, managed identity), SDK setup, calling Graph APIs, batching, delta queries, change notifications, throttling, and permission scopes. Use when accessing Microsoft 365 data (users, mail, calendar, Teams, files, SharePoint) from any app
- Integrate Microsoft Graph SDK into any project - .
- Platform-specific setup patterns for msgraph-sdk.
- Evidence-backed steps from upstream SKILL.md.
- When-to-use criteria for msgraph-sdk versus alternatives.
Msgraph Sdk by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,980 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
msgraph-sdk capabilities & compatibility
- Capabilities
- msgraph sdk quick start · msgraph sdk when to use guidance · msgraph sdk integration patterns
What msgraph-sdk says it does
Use this skill when integrating Microsoft Graph into an application to access Microsoft 365 data and services.
Always ground implementation in the current Microsoft Graph SDK documentation and SDK version for the target language rather than relying on memory alone.
npx skills add https://github.com/github/awesome-copilot --skill msgraph-sdkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 37.5k |
| Last updated | August 5, 2026 |
| Repository | github/awesome-copilot ↗ |
How do I use msgraph-sdk correctly?
Integrate Microsoft Graph SDK into any project - .NET, TypeScript/JavaScript, or Python. Covers auth patterns (client credentials, OBO, managed identity), SDK setup, calling Graph APIs, batching, delt
Who is it for?
Teams implementing msgraph-sdk workflows from the catalog.
Skip if: Skip when requirements clearly match a different specialized stack.
When should I use this skill?
User asks about msgraph-sdk, integrate microsoft graph sdk into any project - .net, typescript/javascript, or python. c.
What you get
Working msgraph-sdk setup with validated configuration and next steps.
Files
Microsoft Graph SDK
Use this skill when integrating Microsoft Graph into an application to access Microsoft 365 data and services.
Always ground implementation in the current Microsoft Graph SDK documentation and SDK version for the target language rather than relying on memory alone.
Determine the target language first
1. Use the .NET workflow when the project contains .cs, .csproj, or .sln files, or when the user asks for C# guidance. Follow references/dotnet.md. 2. Use the TypeScript / JavaScript workflow when the project contains package.json, .ts, or .js files, or when the user asks for Node.js / browser guidance. Follow references/typescript.md. 3. Use the Python workflow when the project contains .py, pyproject.toml, or requirements.txt, or when the user asks for Python guidance. Follow references/python.md. 4. If multiple languages are present, match the language of the files being edited or ask the user.
Always consult live documentation
- Microsoft Graph overview: <https://learn.microsoft.com/graph/overview>
- Graph Explorer (try calls live): <https://developer.microsoft.com/graph/graph-explorer>
- Graph permissions reference: <https://learn.microsoft.com/graph/permissions-reference>
- Use Microsoft Docs MCP tooling when available to fetch current API shapes and SDK samples.
Authentication — choose the right pattern
Selecting the wrong auth flow is the most common Graph integration mistake. Apply this decision tree before writing any auth code:
| Scenario | Flow to use |
|---|---|
| Background service / daemon with no user | Client credentials (app-only) |
| Agent or API acting on behalf of a signed-in user | On-Behalf-Of (OBO) |
| App running in Azure (Function, Container App, VM) | Managed Identity (preferred over secrets) |
| CLI tool or local dev script | Device code or interactive browser |
| Single-page app (browser only) | Authorization code + PKCE |
- Never use client credentials when a user context is required — Graph enforces this at the permission level (application vs. delegated).
- Prefer
DefaultAzureCredentialin Azure-hosted apps; it tries managed identity first and falls back gracefully for local dev. - Never hardcode secrets. Use environment variables, Azure Key Vault, or the Secret Manager.
Core SDK usage patterns
Building the client
Always construct GraphServiceClient once and reuse it (it manages token caching internally).
Pass a credential from the Azure Identity library — never build raw HTTP clients manually.
Making calls
- Use the fluent builder API:
client.Users[userId].Messages.GetAsync(...). - Always
awaitasync calls. - Specify
$selectto limit returned fields — Graph returns large default payloads. - Use
$filterserver-side rather than filtering returned collections in memory. - Use
$expandto fetch related resources in a single call when relationships are small.
Pagination
Graph paginates collections. Never assume all items arrive in one response:
- Check for an
@odata.nextLinkon the response. - Use the SDK's
PageIteratorhelper (available in all three SDKs) to walk pages automatically. - Set
$topto control page size (max varies by resource, typically 999).
Advanced patterns
Batch requests
Combine up to 20 independent Graph calls into a single HTTP request using the $batch endpoint. Use batching when:
- Initializing data for a dashboard or agent that needs multiple resources upfront.
- Reducing latency in high-call-count operations.
Batch responses arrive out of order — match them by the id field you assigned each request.
Delta queries
Use delta queries to sync changes incrementally instead of polling full collections:
- First call:
GET /users/deltareturns all items + a@odata.deltaLink. - Subsequent calls: use the
deltaLinkto receive only what changed since the last sync. - Supported on: users, groups, messages, calendar events, Teams channels, and more.
- Store the
deltaLinkdurably (database, blob) between sync runs.
Change notifications (webhooks)
Subscribe to resource changes with POST /subscriptions:
- Graph delivers change events to your HTTPS notification URL.
- Subscriptions expire — renew them before
expirationDateTime(max varies by resource; typically 1–3 days for mail/calendar, up to 4230 minutes for users/groups). - Validate the subscription handshake: Graph sends a
validationTokenquery parameter on creation — echo it back as plain text with HTTP 200. - Use lifecycle notifications (
notificationUrl+lifecycleNotificationUrl) to handle missed events and reauthorization. - For high-volume scenarios prefer change notifications with resource data (requires additional encryption setup).
Throttling
Graph throttles aggressively. Always handle HTTP 429:
- Read the
Retry-Afterheader — it specifies exact seconds to wait, not a fixed backoff. - The SDK's built-in retry middleware handles 429 automatically when configured; enable it explicitly.
- Avoid fan-out patterns that hit Graph with hundreds of parallel requests; use batching or queuing instead.
Permissions
Get permissions right before writing auth code — wrong scopes result in 403 errors that are hard to debug later.
- Application permissions run without a user (daemon / service). Require admin consent.
- Delegated permissions run in the context of a signed-in user. Some require admin consent.
- Request the minimum permissions needed. Graph's permission reference lists least-privilege options for every operation.
- Use the Graph Explorer to test which permissions a call actually requires before coding.
- In Azure app registrations: grant API permissions → Microsoft Graph → select type (Application or Delegated) → grant admin consent where required.
Common Graph resources — quick reference
| Goal | Resource path |
|---|---|
| Get signed-in user's profile | GET /me |
| List user's mailbox messages | GET /me/messages |
| Send an email | POST /me/sendMail |
| List calendar events | GET /me/events |
| Get user's OneDrive root | GET /me/drive/root/children |
| List Teams the user is in | GET /me/joinedTeams |
| Post a Teams channel message | POST /teams/{id}/channels/{id}/messages |
| List SharePoint site lists | GET /sites/{siteId}/lists |
| Search across M365 | POST /search/query |
| List all users in tenant (app-only) | GET /users |
| Get group members | GET /groups/{id}/members |
In similar fashion, use the SDK's fluent API to navigate to these resources in code.
Workflow
1. Determine the target language and read the matching reference file. 2. Identify the auth scenario and choose the correct flow from the table above. 3. Fetch current SDK docs and Graph Explorer examples before making implementation choices. 4. Apply least-privilege permissions — confirm in the Graph permissions reference. 5. Implement pagination from the start — don't assume single-page responses. 6. Enable retry middleware for throttling from day one. 7. For syncing scenarios, prefer delta queries over polling. 8. Use the language-specific package names, auth provider setup, and code patterns from the chosen reference file.
Completion criteria
- Auth flow matches the scenario (not defaulting to client credentials for user-context calls).
GraphServiceClientis constructed once and reused.- All collection reads handle pagination.
- Throttling (429) is handled via retry middleware or explicit
Retry-Afterlogic. - Permissions are scoped to the minimum required.
- No secrets or credentials are hardcoded.
- Code matches current SDK version patterns for the selected language.
Microsoft Graph SDK for .NET
Use this reference when the target project is written in C# or another .NET language.
Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-dotnet>
- Samples: <https://github.com/microsoftgraph/msgraph-training-dotnet>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/main/CHANGELOG.md>
Packages
<!-- Microsoft Graph SDK v5 (current) -->
<PackageReference Include="Microsoft.Graph" Version="5.*" />
<!-- Azure Identity for credential providers -->
<PackageReference Include="Azure.Identity" Version="1.*" />Install via CLI:
dotnet add package Microsoft.Graph
dotnet add package Azure.IdentityClient setup
Managed Identity (Azure-hosted apps — preferred)
using Azure.Identity;
using Microsoft.Graph;
var credential = new DefaultAzureCredential();
var graphClient = new GraphServiceClient(credential);Client credentials (app-only / daemon)
var credential = new ClientSecretCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
clientId: Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"),
clientSecret: Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET")
);
var graphClient = new GraphServiceClient(credential);Prefer ClientCertificateCredential over ClientSecretCredential in production.
On-Behalf-Of (OBO) — agent / API acting as the signed-in user
// incomingToken is the bearer token received from the caller
var credential = new OnBehalfOfCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
clientId: Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"),
clientSecret: Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET"),
userAssertion: new UserAssertion(incomingToken)
);
var graphClient = new GraphServiceClient(credential);Interactive (local dev / CLI)
var credential = new InteractiveBrowserCredential();
var graphClient = new GraphServiceClient(credential);Common call patterns
Get a resource with field selection
var user = await graphClient.Me.GetAsync(config =>
{
config.QueryParameters.Select = ["displayName", "mail", "jobTitle"];
});List with filter and select
var messages = await graphClient.Me.Messages.GetAsync(config =>
{
config.QueryParameters.Filter = "isRead eq false";
config.QueryParameters.Select = ["subject", "from", "receivedDateTime"];
config.QueryParameters.Top = 25;
config.QueryParameters.Orderby = ["receivedDateTime desc"];
});Pagination with PageIterator
var messages = await graphClient.Me.Messages.GetAsync();
var allMessages = new List<Message>();
var pageIterator = PageIterator<Message, MessageCollectionResponse>
.CreatePageIterator(graphClient, messages, (msg) =>
{
allMessages.Add(msg);
return true; // return false to stop early
});
await pageIterator.IterateAsync();Send an email
await graphClient.Me.SendMail.PostAsync(new SendMailPostRequestBody
{
Message = new Message
{
Subject = "Hello from Graph",
Body = new ItemBody { ContentType = BodyType.Text, Content = "Test message" },
ToRecipients = [new Recipient { EmailAddress = new EmailAddress { Address = "user@contoso.com" } }]
}
});Post a Teams channel message
await graphClient.Teams[teamId].Channels[channelId].Messages.PostAsync(new ChatMessage
{
Body = new ItemBody { ContentType = BodyType.Html, Content = "<b>Hello from Graph!</b>" }
});Batch requests
using Microsoft.Graph.Models;
var batchRequestContent = new BatchRequestContentCollection(graphClient);
var meRequest = await batchRequestContent.AddBatchRequestStepAsync(
graphClient.Me.ToGetRequestInformation());
var messagesRequest = await batchRequestContent.AddBatchRequestStepAsync(
graphClient.Me.Messages.ToGetRequestInformation());
var batchResponse = await graphClient.Batch.PostAsync(batchRequestContent);
var me = await batchResponse.GetResponseByIdAsync<User>(meRequest);
var msgs = await batchResponse.GetResponseByIdAsync<MessageCollectionResponse>(messagesRequest);Delta queries
// First sync — get all + deltaLink
var deltaResponse = await graphClient.Users.Delta.GetAsDeltaGetResponseAsync();
string? deltaLink = null;
var pageIterator = PageIterator<User, Microsoft.Graph.Users.Delta.DeltaGetResponse>
.CreatePageIterator(graphClient, deltaResponse, (user) => { /* process */ return true; },
(req) => { deltaLink = /* extract from response */; return req; });
await pageIterator.IterateAsync();
// Store deltaLink for next run
// Subsequent sync — only changes
// Use the stored deltaLink directly as the next request URLThrottling / retry middleware
The SDK includes retry middleware enabled by default. For explicit control:
var handlers = GraphClientFactory.CreateDefaultHandlers();
// RetryHandler is included; configure max retries if needed
var httpClient = GraphClientFactory.Create(handlers);
var graphClient = new GraphServiceClient(httpClient, credential);Always check Retry-After if building custom retry logic — do not use fixed exponential backoff.
Dependency injection (ASP.NET Core / .NET Worker)
// Program.cs
builder.Services.AddSingleton<GraphServiceClient>(_ =>
{
var credential = new DefaultAzureCredential();
return new GraphServiceClient(credential);
});.NET-specific guidance
- Target .NET 8+ for new projects.
- Use
async/awaitthroughout — all Graph SDK calls are async. - Register
GraphServiceClientas a singleton (it caches tokens internally). - Use
ILoggerto log Graph exceptions — catchODataErrorfor Graph-specific error details. - For ASP.NET Core APIs using OBO, inject the incoming token from
IHttpContextAccessorand construct the credential per-request (not as a singleton).
// Catching Graph errors
try
{
var user = await graphClient.Me.GetAsync();
}
catch (ODataError odataError)
{
Console.WriteLine($"Graph error: {odataError.Error?.Code} - {odataError.Error?.Message}");
}Microsoft Graph SDK for Python
Use this reference when the target project is written in Python.
Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-python>
- Samples: <https://github.com/microsoftgraph/msgraph-training-python>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-python/blob/main/CHANGELOG.md>
Packages
pip install msgraph-sdk azure-identityOr in requirements.txt / pyproject.toml:
msgraph-sdk>=1.0.0
azure-identity>=1.15.0Client setup
Managed Identity (Azure-hosted apps — preferred)
from azure.identity.aio import DefaultAzureCredential
from msgraph import GraphServiceClient
credential = DefaultAzureCredential()
graph_client = GraphServiceClient(credential)The Python SDK is async-first (asyncio). Use azure.identity.aio (async variants), not azure.identity.
Client credentials (app-only / daemon)
import os
from azure.identity.aio import ClientSecretCredential
from msgraph import GraphServiceClient
credential = ClientSecretCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
)
graph_client = GraphServiceClient(credential)Prefer CertificateCredential over ClientSecretCredential in production.
On-Behalf-Of (OBO) — agent / API acting as the signed-in user
from azure.identity.aio import OnBehalfOfCredential
# incoming_token is the bearer token from the caller
credential = OnBehalfOfCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
user_assertion=incoming_token,
)
graph_client = GraphServiceClient(credential)Construct a new GraphServiceClient per request for OBO — the credential is user-scoped.
Device code (CLI / local dev)
from azure.identity.aio import DeviceCodeCredential
credential = DeviceCodeCredential(
client_id=os.environ["AZURE_CLIENT_ID"],
tenant_id=os.environ["AZURE_TENANT_ID"],
)
graph_client = GraphServiceClient(credential, scopes=["User.Read", "Mail.Read"])Common call patterns
All Graph SDK calls in Python are async. Always run inside an async context.
Get a resource with field selection
import asyncio
from msgraph.generated.me.me_request_builder import MeRequestBuilder
from kiota_abstractions.base_request_configuration import RequestConfiguration
async def get_my_profile():
query_params = MeRequestBuilder.MeRequestBuilderGetQueryParameters(
select=["displayName", "mail", "jobTitle"]
)
config = RequestConfiguration(query_parameters=query_params)
user = await graph_client.me.get(request_configuration=config)
return user
asyncio.run(get_my_profile())List messages with filter and select
from msgraph.generated.me.messages.messages_request_builder import MessagesRequestBuilder
async def get_unread_messages():
query_params = MessagesRequestBuilder.MessagesRequestBuilderGetQueryParameters(
filter="isRead eq false",
select=["subject", "from", "receivedDateTime"],
top=25,
orderby=["receivedDateTime desc"],
)
config = RequestConfiguration(query_parameters=query_params)
result = await graph_client.me.messages.get(request_configuration=config)
return resultPagination with PageIterator
from msgraph.generated.models.message import Message
from msgraph.core import PageIterator
async def get_all_messages():
first_page = await graph_client.me.messages.get()
all_messages: list[Message] = []
async def process_message(message: Message) -> bool:
all_messages.append(message)
return True # return False to stop early
page_iterator = PageIterator(
response=first_page,
request_adapter=graph_client.request_adapter,
constructor=Message,
)
await page_iterator.iterate(callback=process_message)
return all_messagesSend an email
from msgraph.generated.models.message import Message
from msgraph.generated.models.item_body import ItemBody
from msgraph.generated.models.body_type import BodyType
from msgraph.generated.models.recipient import Recipient
from msgraph.generated.models.email_address import EmailAddress
from msgraph.generated.me.send_mail.send_mail_post_request_body import SendMailPostRequestBody
async def send_email():
body = SendMailPostRequestBody(
message=Message(
subject="Hello from Graph",
body=ItemBody(content_type=BodyType.Text, content="Test message"),
to_recipients=[
Recipient(email_address=EmailAddress(address="user@contoso.com"))
],
)
)
await graph_client.me.send_mail.post(body)Post a Teams channel message
from msgraph.generated.models.chat_message import ChatMessage
from msgraph.generated.models.item_body import ItemBody
from msgraph.generated.models.body_type import BodyType
async def post_channel_message(team_id: str, channel_id: str):
message = ChatMessage(
body=ItemBody(content_type=BodyType.Html, content="<b>Hello from Graph!</b>")
)
await graph_client.teams.by_team_id(team_id).channels.by_channel_id(channel_id).messages.post(message)Batch requests
from kiota_http.middleware.options import ResponseHandlerOption
import json
async def batch_example():
batch_body = {
"requests": [
{"id": "1", "method": "GET", "url": "/me"},
{"id": "2", "method": "GET", "url": "/me/messages?$top=5&$select=subject"},
]
}
# Use the raw HTTP client for batch
response = await graph_client.request_adapter.send_primitive_async(
# Alternatively, use the requests library with a token from the credential
)For batch in Python, it's often simpler to use httpx with an acquired token when the batch helper is not yet fully supported:
import httpx
from azure.identity.aio import ClientSecretCredential
async def batch_with_httpx(credential):
token = await credential.get_token("https://graph.microsoft.com/.default")
async with httpx.AsyncClient() as client:
response = await client.post(
"https://graph.microsoft.com/v1.0/$batch",
headers={"Authorization": f"Bearer {token.token}"},
json={
"requests": [
{"id": "1", "method": "GET", "url": "/me"},
{"id": "2", "method": "GET", "url": "/me/messages?$top=5"},
]
},
)
return response.json()Delta queries
async def delta_sync(stored_delta_link: str | None = None):
if stored_delta_link:
# Use delta link directly
response = await graph_client.request_adapter.send_async(...)
else:
response = await graph_client.users.delta.get()
users = []
async def collect(user):
users.append(user)
return True
page_iterator = PageIterator(response=response, request_adapter=graph_client.request_adapter, constructor=...)
await page_iterator.iterate(callback=collect)
delta_link = page_iterator.delta_link # store this for next run
return users, delta_linkThrottling / retry
The SDK's HTTP transport handles 429 retry automatically when using the default GraphClientFactory. For explicit control:
import asyncio
import httpx
async def call_with_retry(graph_client, call_fn, max_retries=5):
for attempt in range(max_retries):
try:
return await call_fn()
except Exception as e:
if "429" in str(e):
retry_after = int(getattr(e, "retry_after", 10))
await asyncio.sleep(retry_after)
else:
raisePython-specific guidance
- The Python Graph SDK is async-first — use
asyncio.run()or an async framework (FastAPI, aiohttp). - Always use
azure.identity.aio(notazure.identity) for async contexts. - Close credentials when done:
await credential.close()or use as async context managers. - Python SDK model classes use
snake_casefor properties (Graph JSON usescamelCase— the SDK maps automatically). - Use
asyncio.gather()for concurrent but independent Graph calls (mind throttling limits). - For FastAPI: use lifespan events to init
GraphServiceClientonce and close the credential on shutdown.
# FastAPI integration example
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
credential = DefaultAzureCredential()
app.state.graph_client = GraphServiceClient(credential)
yield
await credential.close()
app = FastAPI(lifespan=lifespan)Microsoft Graph SDK for TypeScript / JavaScript
Use this reference when the target project uses TypeScript or JavaScript (Node.js or browser).
Authoritative sources
- SDK repository: <https://github.com/microsoftgraph/msgraph-sdk-javascript>
- Samples: <https://github.com/microsoftgraph/msgraph-training-typescript>
- SDK changelog: <https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/main/CHANGELOG.md>
Packages
npm install @microsoft/microsoft-graph-client @azure/identity
npm install -D @microsoft/microsoft-graph-types # TypeScript type definitionsFor Node.js environments, also install the fetch polyfill:
npm install node-fetchClient setup
Managed Identity (Azure-hosted apps — preferred)
import { Client } from "@microsoft/microsoft-graph-client";
import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials/index.js";
import { DefaultAzureCredential } from "@azure/identity";
const credential = new DefaultAzureCredential();
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });Client credentials (app-only / daemon)
import { ClientSecretCredential } from "@azure/identity";
const credential = new ClientSecretCredential(
process.env.AZURE_TENANT_ID!,
process.env.AZURE_CLIENT_ID!,
process.env.AZURE_CLIENT_SECRET!
);
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });On-Behalf-Of (OBO) — agent / API acting as the signed-in user
import { OnBehalfOfCredential } from "@azure/identity";
// incomingToken is the bearer token received from the caller (e.g. from req.headers.authorization)
const credential = new OnBehalfOfCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
clientSecret: process.env.AZURE_CLIENT_SECRET!,
userAssertionToken: incomingToken,
});
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ["https://graph.microsoft.com/.default"],
});
const graphClient = Client.initWithMiddleware({ authProvider });For OBO, create a new client per request (credential is user-scoped, not singleton-safe).
Interactive (local dev / CLI — Node.js)
Use InteractiveBrowserCredential when a browser is available. Use DeviceCodeCredential for headless environments (SSH, CI-adjacent, WSL):
import { InteractiveBrowserCredential, DeviceCodeCredential } from "@azure/identity";
// Opens a browser tab — requires redirect URI http://localhost in app registration
const credential = new InteractiveBrowserCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
});
// Prints a device code to the terminal — works in any environment
const credential = new DeviceCodeCredential({
tenantId: process.env.AZURE_TENANT_ID!,
clientId: process.env.AZURE_CLIENT_ID!,
userPromptCallback: (info) => console.log(info.message),
});Both require the app registration platform to be "Mobile and desktop applications". Neither uses a client secret.
Common call patterns
Get a resource with field selection
import { User } from "@microsoft/microsoft-graph-types";
const user: User = await graphClient
.api("/me")
.select("displayName,mail,jobTitle")
.get();List with filter, select, and ordering
const result = await graphClient
.api("/me/messages")
.filter("isRead eq false")
.select("subject,from,receivedDateTime")
.top(25)
.orderby("receivedDateTime desc")
.get();Pagination with PageIterator
import { PageIterator } from "@microsoft/microsoft-graph-client";
import { Message } from "@microsoft/microsoft-graph-types";
const firstPage = await graphClient.api("/me/messages").top(25).get();
const allMessages: Message[] = [];
const pageIterator = new PageIterator(
graphClient,
firstPage,
(message: Message) => {
allMessages.push(message);
return true; // return false to stop early
}
);
await pageIterator.iterate();Send an email
await graphClient.api("/me/sendMail").post({
message: {
subject: "Hello from Graph",
body: { contentType: "Text", content: "Test message" },
toRecipients: [{ emailAddress: { address: "user@contoso.com" } }],
},
});Post a Teams channel message
await graphClient.api(`/teams/${teamId}/channels/${channelId}/messages`).post({
body: { contentType: "html", content: "<b>Hello from Graph!</b>" },
});Upload a file to OneDrive (small files ≤ 4 MB)
const content = Buffer.from("file contents");
await graphClient
.api(`/me/drive/root:/${fileName}:/content`)
.putStream(content);For files > 4 MB, use an upload session (createUploadSession).
Batch requests
const batchRequestBody = {
requests: [
{ id: "1", method: "GET", url: "/me" },
{ id: "2", method: "GET", url: "/me/messages?$top=5&$select=subject" },
],
};
const batchResponse = await graphClient.api("/$batch").post(batchRequestBody);
const meResponse = batchResponse.responses.find((r: any) => r.id === "1");
const messagesResponse = batchResponse.responses.find((r: any) => r.id === "2");Delta queries
// First sync
let response = await graphClient.api("/users/delta").get();
const users: any[] = [];
while (response["@odata.nextLink"]) {
users.push(...response.value);
response = await graphClient.api(response["@odata.nextLink"]).get();
}
users.push(...response.value);
const deltaLink: string = response["@odata.deltaLink"];
// Store deltaLink durably for next sync run
// Next sync — only changes
const changesResponse = await graphClient.api(deltaLink).get();Throttling / retry middleware
The SDK includes retry middleware by default. For explicit configuration:
import {
Client,
RetryHandlerOptions,
RetryHandler,
MiddlewareFactory,
} from "@microsoft/microsoft-graph-client";
const retryOptions = new RetryHandlerOptions({ maxRetries: 5 });
const middleware = MiddlewareFactory.getDefaultMiddlewareChain(authProvider);
const graphClient = Client.initWithMiddleware({ middleware });Always honour the Retry-After header value — do not use fixed backoff when Graph specifies a wait time.
TypeScript-specific guidance
- Import types from
@microsoft/microsoft-graph-typesfor full IntelliSense on Graph resources. - The
.api()chain returnsany— cast to the appropriate type from@microsoft/microsoft-graph-types. - For ESM projects, use the
/index.jspath suffix on deep imports (e.g.,azureTokenCredentials/index.js). - Use
async/awaitconsistently — all Graph calls return Promises. - Singleton the
graphClientin application-level code (e.g., Express app init); for OBO flows, construct per-request. - In Node.js 18+,
fetchis available natively — no polyfill needed.
// Type-safe response example
import { MessageCollectionResponse } from "@microsoft/microsoft-graph-types";
const response: MessageCollectionResponse = await graphClient
.api("/me/messages")
.select("subject,from")
.get();
const messages = response.value ?? [];Related skills
FAQ
What does msgraph-sdk do?
msgraph-sdk skill documents Integrate Microsoft Graph SDK into any project - .
When should I use msgraph-sdk?
User asks about msgraph-sdk, integrate microsoft graph sdk into any project - .net, typescript/javascript, or python. c.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.