DotNetCode, part 5: Four Azure Function relays — Graph email, SmartyStreets, Shopify, Dataverse
Half of DotNetCode's projects follow one architecture so consistently that the repetition itself is the lesson. SendingEmailViaMicrosoftGraph, SmartyStreetsLookup, ShopifyIntegration and MicrosoftCrmIntegration each integrate a SaaS API — and each is built the same three-part way. This article covers the pattern; the repositories are under github.com/bobhuang1/DotNetCode.
The three parts
- An Azure Function relay (.NET 10, isolated worker) — your own HTTP surface in front of the vendor API. It holds the vendor credentials (in Azure Key Vault via a credential-provider interface), exposes a small typed endpoint, and becomes the single place where rate limits, retries and logging live.
- Client samples — console programs showing how callers use the relay.
- Standalone class libraries — for callers who would rather skip the relay and hit the vendor API directly. Because the Azure Functions isolated model is .NET 10-only, these ship as separate libraries per runtime (.NET Framework vs .NET 6+) — the honest answer when a dependency breaks the multi-targeting rule.
What each integration does
- SendingEmailViaMicrosoftGraph — sends email through Microsoft Graph instead of SMTP: modern auth, no port 25, per-mailbox permissions, and a domain-to-recipient mapping the caller can configure.
- SmartyStreetsLookup — US/international address validation: enter a messy address, get the corrected, standardised, ZIP+4 version back.
- ShopifyIntegration — generic CRUD over the Shopify Admin REST API for customers, orders, draft orders and products — one generic client rather than four bespoke ones.
- MicrosoftCrmIntegration — generic CRUD against the Dataverse Web API for any table (accounts, contacts, incidents, price lists), with the organisation-specific rules exposed as parameters and delegates.
Why a relay at all?
Direct calls are simpler — until the credential leaks, the vendor changes an endpoint, or you need one audit trail. The relay costs one function app and buys a security boundary, a versioning seam and centralised observability. Whether that trade is worth it is exactly the kind of judgement the samples are designed to teach.
Repository: github.com/bobhuang1/DotNetCode