Learn how to navigate the Microsoft Graph API using strongly-typed request builders
Request builders provide a fluent, strongly-typed way to navigate and interact with the Microsoft Graph API. They mirror the REST API structure and enable compile-time checking of API paths and operations.
Retrieve a single resource using the GetAsync method:
// Get current uservar user = await graphClient.Me.GetAsync();Console.WriteLine($"Display Name: {user.DisplayName}");Console.WriteLine($"Email: {user.Mail}");Console.WriteLine($"Job Title: {user.JobTitle}");
// Get all messagesvar messages = await graphClient.Me.Messages.GetAsync();foreach (var message in messages.Value){ Console.WriteLine($"{message.Subject} - {message.ReceivedDateTime}");}
// Get specific user by IDvar user = await graphClient.Users["user-id"].GetAsync();// Get specific message by IDvar message = await graphClient.Me.Messages["message-id"].GetAsync();// Get specific group by IDvar group = await graphClient.Groups["group-id"].GetAsync();
Maps to REST API:
GET https://graph.microsoft.com/v1.0/users/user-idGET https://graph.microsoft.com/v1.0/me/messages/message-idGET https://graph.microsoft.com/v1.0/groups/group-id
From GroupsRequestBuilder.cs:55-62:
public global::Microsoft.Graph.Groups.Item.GroupItemRequestBuilder this[string position]{ get { var urlTplParams = new Dictionary<string, object>(PathParameters); urlTplParams.Add("group%2Did", position); return new global::Microsoft.Graph.Groups.Item.GroupItemRequestBuilder(urlTplParams, RequestAdapter); }}
Chain request builders to navigate resource relationships:
// Get user's calendarvar calendar = await graphClient.Me.Calendar.GetAsync();// Get user's managervar manager = await graphClient.Me.Manager.GetAsync();// Get group membersvar members = await graphClient.Groups["group-id"].Members.GetAsync();// Get user's drive itemsvar driveItems = await graphClient.Me.Drive.Items.GetAsync();
From MeRequestBuilder.cs:131-135:
public global::Microsoft.Graph.Me.Calendar.CalendarRequestBuilder Calendar{ get => new global::Microsoft.Graph.Me.Calendar.CalendarRequestBuilder(PathParameters, RequestAdapter);}
using Microsoft.Graph.Models;// Create a new messagevar newMessage = new Message{ Subject = "Hello from Graph SDK", Body = new ItemBody { ContentType = BodyType.Text, Content = "This is a test message." }, ToRecipients = new List<Recipient> { new Recipient { EmailAddress = new EmailAddress { Address = "[email protected]" } } }};var message = await graphClient.Me.Messages.PostAsync(newMessage);// Create a new groupvar newGroup = new Group{ DisplayName = "Marketing Team", MailEnabled = false, MailNickname = "marketing", SecurityEnabled = true};var group = await graphClient.Groups.PostAsync(newGroup);
Method signature from GroupsRequestBuilder.cs:115:
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));try{ var users = await graphClient.Users.GetAsync( requestConfiguration: null, cancellationToken: cts.Token );}catch (OperationCanceledException){ Console.WriteLine("Request was cancelled or timed out");}
// Reminder view with date rangevar reminders = await graphClient.Me .ReminderViewWithStartDateTimeWithEndDateTime( "2024-01-01T00:00:00Z", "2024-12-31T23:59:59Z" ) .GetAsync();
Method from MeRequestBuilder.cs:575-580:
public global::Microsoft.Graph.Me.ReminderViewWithStartDateTimeWithEndDateTime .ReminderViewWithStartDateTimeWithEndDateTimeRequestBuilder ReminderViewWithStartDateTimeWithEndDateTime(string endDateTime, string startDateTime){ if(string.IsNullOrEmpty(endDateTime)) throw new ArgumentNullException(nameof(endDateTime)); if(string.IsNullOrEmpty(startDateTime)) throw new ArgumentNullException(nameof(startDateTime)); return new ...ReminderViewWithStartDateTimeWithEndDateTimeRequestBuilder(...);}
// Only get user ID - other properties will be nullvar user = await graphClient.Me.GetAsync(config => config.QueryParameters.Select = new[] { "id" });Console.WriteLine($"ID: {user.Id}"); // Has valueConsole.WriteLine($"Name: {user.DisplayName}"); // null
// Get first pagevar messages = await graphClient.Me.Messages.GetAsync(config =>{ config.QueryParameters.Top = 10;});foreach (var message in messages.Value){ Console.WriteLine(message.Subject);}// Check for next pageif (messages.OdataNextLink != null){ // Get next page using the next link // See Collections documentation for detailed pagination examples}
// Good - only get what you needvar user = await graphClient.Me.GetAsync(config => config.QueryParameters.Select = new[] { "id", "displayName", "mail" });// Avoid - gets all propertiesvar user = await graphClient.Me.GetAsync();
Use appropriate HTTP methods
Use the correct method for your operation:
GetAsync() - Read data (idempotent, safe)
PostAsync() - Create resources
PatchAsync() - Update resources (partial update)
DeleteAsync() - Remove resources
Avoid using POST for updates - use PATCH instead.
Handle null values
Properties may be null when not selected or not available:
var user = await graphClient.Me.GetAsync(config => config.QueryParameters.Select = new[] { "displayName" });// Good - null checkvar email = user.Mail ?? "No email";// Bad - may throw NullReferenceExceptionvar email = user.Mail.ToLower();
Use cancellation tokens
Support request cancellation for better responsiveness:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));var users = await graphClient.Users.GetAsync( requestConfiguration: config => config.QueryParameters.Top = 100, cancellationToken: cts.Token);
Reuse request builders for similar requests
Request builders are lightweight - they don’t execute until you call an operation method:
var messagesBuilder = graphClient.Me.Messages;// Get unread messagesvar unread = await messagesBuilder.GetAsync(config => config.QueryParameters.Filter = "isRead eq false");// Get important messagesvar important = await messagesBuilder.GetAsync(config => config.QueryParameters.Filter = "importance eq 'high'");