Elixir client for the Microsoft Graph API.
GraphApi provides a clean, idiomatic interface to Microsoft Graph API resources with built-in authentication, OData query building, batch requests, pagination, schema casting, and error handling.
Architecture Overview
┌─────────────────────────────────────────────────────────┐
│ YOUR APPLICATION │
└─────────────────────────────────────────────────────────┘
│
┌──────────────┴──────────────┐
│ │
▼ ▼
┌───────────────────┐ ┌───────────────────┐
│ App-Only Auth │ │ Delegated Auth │
│ (Client Creds) │ │ (User OAuth Token) │
│ │ │ │
│ Config + secret → │ │ access_token per │
│ auto token mgmt │ │ request │
└─────────┬─────────┘ └─────────┬─────────┘
│ │
└─────────────┬─────────────┘
│
▼
┌───────────────────────────┐
│ GraphApi.Client │
│ Req.Request + middleware │
└─────────────┬─────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Auth │ │ Error │ │ Retry │
│Middleware│ │ Handling │ │Middleware│
└──────────┘ └──────────┘ └──────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ RESOURCE MODULES │
│ │
│ Users · Groups · Mail · Calendar · Files · Subscriptions│
│ │
│ Each function returns {:ok, result} | {:error, error} │
│ Each has a _query variant for batch requests │
└─────────────────────────┬───────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ OData │ │ Batch │ │ Delta │
│ Query │ │ Requests │ │ Queries │
│ Builder │ │ (up to │ │ (change │
│ + Filter │ │ 20/call)│ │ tracking)│
└──────────┘ └──────────┘ └──────────┘
│ │ │
└─────────────┼─────────────┘
│
▼
┌───────────────────────────┐
│ Schema Casting │
│ JSON → typed structs │
│ v1.0 + Beta schemas │
│ Views for projections │
└───────────────────────────┘Authentication Modes
App-only (client credentials) — configure once, tokens managed automatically:
config :keen_microsoft_graphapi, :config,
tenant_id: System.fetch_env!("AZURE_TENANT_ID"),
client_id: System.fetch_env!("AZURE_CLIENT_ID"),
client_secret: System.fetch_env!("AZURE_CLIENT_SECRET")
{:ok, users} = GraphApi.Users.list()Delegated (user token) — pass an OAuth access token per request:
client = GraphApi.Client.new()
{:ok, me} = GraphApi.Users.get("me", client: client, access_token: user_token)Multi-tenant — explicit config per tenant:
config = GraphApi.Config.new!(tenant_id: "aaa", client_id: "bbb", client_secret: "ccc")
client = GraphApi.Client.new(config: config)
{:ok, users} = GraphApi.Users.list(client: client)Resource Modules
| Module | Endpoints | Description |
|---|---|---|
GraphApi.Users | 30 | CRUD, manager, memberships, licenses, photos, auth methods |
GraphApi.Groups | 26 | CRUD, members, owners, app roles, permission grants |
GraphApi.Mail | 9 | Messages, send, drafts, folders, delta |
GraphApi.Calendar | 8 | Events CRUD, calendar view, calendars list |
GraphApi.Files | 23 | Drives, items, upload/download, permissions, versions |
GraphApi.Subscriptions | 5 | Webhook subscriptions CRUD |
Every function has a _query variant that returns a %GraphApi.Batch.Request{}
for use in batch operations.
Query Building
alias GraphApi.OData
alias GraphApi.OData.Filter
alias GraphApi.Schema.User
# Functional OData builder
query = OData.new()
|> OData.select(["displayName", "mail"])
|> OData.filter(User, account_enabled: true)
|> OData.top(25)
# Or build complex filters
filter = Filter.new(User)
|> Filter.where(:display_name, :starts_with, "A")
|> Filter.or_where(:department, :eq, "Engineering")
{:ok, response} = GraphApi.Users.list(query: OData.new() |> OData.filter(filter))Error Handling
All operations return {:ok, result}, :ok, or {:error, error}:
GraphApi.Error.ApiError— non-2xx Graph API responsesGraphApi.Error.AuthError— authentication failuresGraphApi.Error.RateLimitError— 429 after retry exhaustion (withretry_after)
Key Features
- Batch requests — up to 20 requests per HTTP call via
GraphApi.Batch - Delta queries — incremental change tracking via
GraphApi.Delta - Pagination — lazy streaming via
GraphApi.Pagination - Schema casting — auto-cast JSON to typed structs with the
:asoption - Views — field projections that auto-inject
$selectviaGraphApi.View - Webhooks — parse and validate Graph notifications via
GraphApi.Webhook