Skip to main content
The GraphServiceClient is the main entry point for all Microsoft Graph SDK operations. It manages authentication, HTTP communication, and provides access to all API endpoints through strongly-typed request builders.

Overview

The GraphServiceClient class is defined in the Microsoft.Graph namespace and inherits from BaseGraphServiceClient. It implements IBaseClient and IDisposable, making it suitable for dependency injection and proper resource management.
GraphServiceClient.cs:21-22
using Microsoft.Graph;

public class GraphServiceClient : BaseGraphServiceClient, IBaseClient, IDisposable

Basic Initialization

The simplest way to create a client using Azure.Identity credentials:
using Azure.Identity;
using Microsoft.Graph;

string[] scopes = { "User.Read" };
var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions
{
    ClientId = "YOUR_CLIENT_ID"
});

var graphClient = new GraphServiceClient(credential, scopes);
Constructor signature from GraphServiceClient.cs:46-52:
public GraphServiceClient(
    TokenCredential tokenCredential,
    IEnumerable<string> scopes = null,
    string baseUrl = null
)
tokenCredential
TokenCredential
required
Azure.Identity TokenCredential for authentication (e.g., InteractiveBrowserCredential, ClientSecretCredential)
scopes
IEnumerable<string>
default:"null"
Permission scopes required for your application (e.g., ["User.Read", "Mail.Send"])
baseUrl
string
default:"https://graph.microsoft.com/v1.0"
Base URL for the Microsoft Graph API. Use https://graph.microsoft.com/beta for beta endpoints

Using TokenCredential with Custom HttpClient

Provide your own configured HttpClient for custom middleware, proxies, or timeouts:
using Azure.Identity;
using Microsoft.Graph;
using System.Net.Http;

var httpClient = new HttpClient
{
    Timeout = TimeSpan.FromSeconds(30)
};

string[] scopes = { "User.Read" };
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);

var graphClient = new GraphServiceClient(
    httpClient,
    credential,
    scopes
);
Constructor signature from GraphServiceClient.cs:61-67:
public GraphServiceClient(
    HttpClient httpClient,
    TokenCredential tokenCredential,
    IEnumerable<string> scopes = null,
    string baseUrl = null
)
When providing a custom HttpClient, you’re responsible for its lifecycle. Consider using IHttpClientFactory in ASP.NET Core applications.

Using IAuthenticationProvider

For custom authentication implementations:
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions.Authentication;

public class CustomAuthProvider : IAuthenticationProvider
{
    public Task AuthenticateRequestAsync(
        RequestInformation request,
        Dictionary<string, object> additionalAuthenticationContext = null,
        CancellationToken cancellationToken = default)
    {
        // Add authentication header
        request.Headers.Add("Authorization", $"Bearer {GetToken()}");
        return Task.CompletedTask;
    }
    
    private string GetToken() => // Your token acquisition logic
}

var authProvider = new CustomAuthProvider();
var graphClient = new GraphServiceClient(authProvider);
Constructor signature from GraphServiceClient.cs:75-79:
public GraphServiceClient(
    IAuthenticationProvider authenticationProvider,
    string baseUrl = null
)

Using IRequestAdapter

For advanced scenarios with full control over request processing:
using Microsoft.Graph;
using Microsoft.Kiota.Abstractions;

var requestAdapter = new CustomRequestAdapter(authProvider);
var graphClient = new GraphServiceClient(requestAdapter);
Constructor signature from GraphServiceClient.cs:33-38:
public GraphServiceClient(
    IRequestAdapter requestAdapter,
    string baseUrl = null
)

Accessing API Endpoints

The GraphServiceClient provides properties for accessing all Microsoft Graph endpoints through request builders:

User Context (Me)

// Get current user
var me = await graphClient.Me.GetAsync();
Console.WriteLine($"Display Name: {me.DisplayName}");

// Get user's messages
var messages = await graphClient.Me.Messages.GetAsync();

// Get user's calendar
var calendar = await graphClient.Me.Calendar.GetAsync();

// Get user's manager
var manager = await graphClient.Me.Manager.GetAsync();

Users Collection

// List all users
var users = await graphClient.Users.GetAsync();

// Get specific user by ID
var user = await graphClient.Users["user-id"].GetAsync();

// Get user by user principal name
var user = await graphClient.Users["[email protected]"].GetAsync();

Groups Collection

// List all groups
var groups = await graphClient.Groups.GetAsync();

// Get specific group
var group = await graphClient.Groups["group-id"].GetAsync();

// Get group members
var members = await graphClient.Groups["group-id"].Members.GetAsync();

Other Common Endpoints

// Sites
var sites = await graphClient.Sites.GetAsync();
var site = await graphClient.Sites["site-id"].GetAsync();

// Teams
var teams = await graphClient.Teams.GetAsync();
var team = await graphClient.Teams["team-id"].GetAsync();

// Applications
var apps = await graphClient.Applications.GetAsync();

// Drives (OneDrive)
var drives = await graphClient.Me.Drives.GetAsync();
var drive = await graphClient.Me.Drive.GetAsync();

// Directory Objects
var directoryObjects = await graphClient.DirectoryObjects.GetAsync();

Configuration Options

Changing Base URL

Access beta API endpoints:
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
string[] scopes = { "https://graph.microsoft.com/.default" };

var graphClient = new GraphServiceClient(
    credential,
    scopes,
    "https://graph.microsoft.com/beta"
);

// Now calls beta endpoint
var user = await graphClient.Me.GetAsync();
Beta endpoints may have different schemas and are subject to change. Use v1.0 for production applications.

Using National Clouds

Connect to sovereign clouds:
// China
var graphClient = new GraphServiceClient(
    credential,
    scopes,
    "https://microsoftgraph.chinacloudapi.cn/v1.0"
);

// US Government
var graphClient = new GraphServiceClient(
    credential,
    scopes,
    "https://graph.microsoft.us/v1.0"
);

// Germany
var graphClient = new GraphServiceClient(
    credential,
    scopes,
    "https://graph.microsoft.de/v1.0"
);

Batch Requests

The client provides access to batch request functionality:
using Microsoft.Graph;

var graphClient = new GraphServiceClient(credential, scopes);

// Access batch request builder
var batchBuilder = graphClient.Batch;

// Batch requests allow multiple operations in a single HTTP call
// See batch documentation for detailed examples
Property definition from GraphServiceClient.cs:104-110:
public BatchRequestBuilder Batch
{
    get
    {
        return new CustomBatchRequestBuilder(this.RequestAdapter);
    }
}

Advanced Scenarios

Custom HTTP Client with Middleware

using System.Net.Http;
using Microsoft.Graph;
using Azure.Identity;

var handler = new HttpClientHandler
{
    Proxy = new WebProxy("http://proxy.company.com:8080"),
    UseProxy = true
};

var httpClient = new HttpClient(handler)
{
    Timeout = TimeSpan.FromSeconds(60)
};

var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
string[] scopes = { "https://graph.microsoft.com/.default" };

var graphClient = new GraphServiceClient(httpClient, credential, scopes);

Accessing Request Adapter

The underlying request adapter for advanced customization:
var graphClient = new GraphServiceClient(credential, scopes);

// Access the request adapter
var requestAdapter = graphClient.RequestAdapter;

// Modify base URL dynamically
requestAdapter.BaseUrl = "https://graph.microsoft.com/beta";
Property definition from GraphServiceClient.cs:99:
public new IRequestAdapter RequestAdapter { get; set; }

Dependency Injection in ASP.NET Core

Program.cs
using Azure.Identity;
using Microsoft.Graph;

var builder = WebApplication.CreateBuilder(args);

// Register as singleton
builder.Services.AddSingleton<GraphServiceClient>(sp =>
{
    var configuration = sp.GetRequiredService<IConfiguration>();
    
    var credential = new ClientSecretCredential(
        configuration["AzureAd:TenantId"],
        configuration["AzureAd:ClientId"],
        configuration["AzureAd:ClientSecret"]
    );
    
    string[] scopes = { "https://graph.microsoft.com/.default" };
    return new GraphServiceClient(credential, scopes);
});

var app = builder.Build();
Controller.cs
public class UsersController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;
    
    public UsersController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    [HttpGet("me")]
    public async Task<IActionResult> GetMe()
    {
        var user = await _graphClient.Me.GetAsync();
        return Ok(user);
    }
}
GraphServiceClient is thread-safe and designed to be long-lived. Register it as a singleton in DI containers.

Disposing the Client

The client implements IDisposable for proper resource cleanup:
using (var graphClient = new GraphServiceClient(credential, scopes))
{
    var user = await graphClient.Me.GetAsync();
    // Client automatically disposed
}

// Or manually
var graphClient = new GraphServiceClient(credential, scopes);
try
{
    var user = await graphClient.Me.GetAsync();
}
finally
{
    graphClient.Dispose();
}
Implementation from GraphServiceClient.cs:115-121:
public void Dispose()
{
    if (this.RequestAdapter is IDisposable disposable)
    {
        disposable.Dispose();
    }
}
Only dispose the client when you’re done with it for the lifetime of your application. Don’t dispose between requests.

Best Practices

Create one GraphServiceClient instance and reuse it throughout your application:
// Good - reuse instance
private static GraphServiceClient _graphClient = 
    new GraphServiceClient(credential, scopes);

public async Task Method1()
{
    var user = await _graphClient.Me.GetAsync();
}

public async Task Method2()
{
    var messages = await _graphClient.Me.Messages.GetAsync();
}
// Bad - creates new instance every call
public async Task Method1()
{
    var client = new GraphServiceClient(credential, scopes);
    var user = await client.Me.GetAsync();
}
Register the client in your DI container as a singleton:
services.AddSingleton<GraphServiceClient>(sp =>
    new GraphServiceClient(credential, scopes));
This ensures:
  • Single instance across application
  • Proper lifecycle management
  • Easy testing with mocks
  • Consistent configuration
Wrap client operations in try-catch blocks:
try
{
    var user = await graphClient.Me.GetAsync();
}
catch (ODataError ex) when (ex.ResponseStatusCode == 401)
{
    // Handle authentication failure
    Console.WriteLine("Authentication failed. Token may be expired.");
}
catch (ODataError ex)
{
    Console.WriteLine($"Graph API error: {ex.Error?.Code}");
}
Set reasonable timeouts for your scenarios:
// For long-running operations
var httpClient = new HttpClient
{
    Timeout = TimeSpan.FromMinutes(5)
};

var graphClient = new GraphServiceClient(
    httpClient,
    credential,
    scopes
);
Request only the permissions you need:
// Good - specific permissions
string[] scopes = { "User.Read", "Mail.Send" };

// Avoid - overly broad
string[] scopes = { "https://graph.microsoft.com/.default" };

Common Patterns

Factory Pattern

public interface IGraphClientFactory
{
    GraphServiceClient CreateClient();
}

public class GraphClientFactory : IGraphClientFactory
{
    private readonly IConfiguration _configuration;
    
    public GraphClientFactory(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    
    public GraphServiceClient CreateClient()
    {
        var credential = new ClientSecretCredential(
            _configuration["AzureAd:TenantId"],
            _configuration["AzureAd:ClientId"],
            _configuration["AzureAd:ClientSecret"]
        );
        
        string[] scopes = { "https://graph.microsoft.com/.default" };
        return new GraphServiceClient(credential, scopes);
    }
}

Multi-Tenant Pattern

public class MultiTenantGraphService
{
    private readonly ConcurrentDictionary<string, GraphServiceClient> _clients = new();
    
    public GraphServiceClient GetClientForTenant(string tenantId)
    {
        return _clients.GetOrAdd(tenantId, tid =>
        {
            var credential = new ClientSecretCredential(
                tid,
                clientId,
                clientSecret
            );
            
            string[] scopes = { "https://graph.microsoft.com/.default" };
            return new GraphServiceClient(credential, scopes);
        });
    }
}

Troubleshooting

Client Not Initialized

// Error: NullReferenceException
// Cause: Client is null

GraphServiceClient graphClient = null;
var user = await graphClient.Me.GetAsync(); // Throws!

// Solution: Always initialize before use
var graphClient = new GraphServiceClient(credential, scopes);

Authentication Failures

// Error: 401 Unauthorized
// Cause: Invalid credentials or expired token

try
{
    var user = await graphClient.Me.GetAsync();
}
catch (ODataError ex) when (ex.ResponseStatusCode == 401)
{
    // Token may be expired or invalid
    // TokenCredential implementations handle refresh automatically
    // If this fails, check credential configuration
}

Base URL Issues

// Error: 404 Not Found
// Cause: Wrong base URL or endpoint not available in v1.0

// Solution: Check if endpoint is beta-only
var graphClient = new GraphServiceClient(
    credential,
    scopes,
    "https://graph.microsoft.com/beta" // Use beta for preview features
);

Next Steps

Request Builders

Learn how to navigate the API with request builders

Query Parameters

Customize requests with query parameters

Error Handling

Handle errors and exceptions properly

Collections

Work with collections and pagination

Additional Resources

Build docs developers (and LLMs) love