Shipping Microsoft Graph on the edge
A field guide to certificate-based app auth, token caching, and the quiet failure modes nobody documents.
Microsoft Graph is one of those APIs that is easy to demo and surprisingly fiddly to operate. The Quickstart will get you a token in eight lines. Getting it to behave under load, on an edge runtime, with rotating credentials and proper error semantics, is a different exercise.
Why certificate auth
Client secrets are convenient and indefensible. They live in environment variables, they get copied into Slack messages, they are rotated reluctantly. Certificate-based credentials cost about ninety minutes to set up once and remove an entire category of incident from your future.
const jwt = await new SignJWT({ aud: tokenEndpoint, iss: clientId, sub: clientId, jti: crypto.randomUUID() })\n .setProtectedHeader({ alg: 'RS256', x5t: thumbprintBase64Url })\n .setIssuedAt()\n .setExpirationTime('5m')\n .sign(privateKey);Caching the access token
A naive implementation acquires a new token per request. Graph will tolerate this for a while and then quietly start throttling you. Cache the token — but cache it at the request scope when you are running on a Worker runtime, because module-level caches do not survive isolate recycling and pretending they do will lead to bizarre intermittent failures.
Handling 429 like an adult
A 429 from Graph is not an error to be retried, it is feedback about the tenant. The right response is to back off every Graph call for the duration of the Retry-After header, not just the one that tripped the limit. Otherwise you spend the next sixty seconds discovering each endpoint's rate limit individually.
Drew leads Jonsen LLC — a Denver technology practice guiding law firms and growing businesses through AI, cybersecurity, and systems that compound over time.
