The one major object store with no S3 compatibility at all — different protocol, different signature, containers instead of buckets. That is why it needs its own adapter rather than an endpoint in the S3 provider, and it is the gap that stopped a Microsoft-shop deployment running Apograph at all.
npm install @apograph/media-provider-azureimport { createAzureStorageProvider } from '@apograph/media-provider-azure';
MediaServerPlugin({
provider: createAzureStorageProvider(config.plugins.media.storage),
config: config.plugins.media
});Configuration
{ container, connectionString?, accountName?, accountKey?, keyPrefix?, containerClient? }
| Variable | What it does |
|---|---|
MEDIA_AZURE_CONTAINER | The container name. Required |
MEDIA_AZURE_CONNECTION_STRING | What the portal hands you, and what Azurite prints |
Three ways to connect
In the order deployments reach for them:
connectionString— the usual one.accountName+accountKey.containerClient— an already-built client.
The third is the escape hatch for managed identity: build a client with
DefaultAzureCredential from @azure/identity and pass it, and this package
stays free of that dependency.
import { DefaultAzureCredential } from '@azure/identity';
import { BlobServiceClient } from '@azure/storage-blob';
const containerClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net`,
new DefaultAzureCredential()
).getContainerClient('apograph-media');
createAzureStorageProvider({ container: 'apograph-media', containerClient });It is also the seam the package’s own tests inject through, so they drive the same entry point a real caller uses rather than a private hook.
directUrl is computed, not hardcoded
Managed identity cannot sign a SAS
A SAS token needs a shared key to sign with. A deployment on managed
identity has none, so directUrl is false there and its downloads are
proxied.
Declaring true unconditionally would let the plugin accept
MEDIA_DIRECT_SERVE=signed-url on a deployment that cannot honour it — and the
failure would land per request rather than at boot, which is exactly what that
design exists to prevent.
When it can sign, the SAS pins rscd and rsct — content disposition and
content type — the same rule every signing provider follows, for the same
reason: a redirect discards the app’s own headers, and the stored MIME type is
the uploader’s claim.
A user-delegation SAS would let a managed identity sign. It needs a delegation key fetched from the service, so it is a follow-up rather than a silent assumption.
What not to lose in a refactor
A failed upload deletes the blob. Azure charges for uncommitted blocks and does not list them, and the key never reached a caller, so nothing else can reclaim them.
A source error reaches the meter. Without the forwarded error, an upload whose source dies waits forever on a stream that will never end.
get maps BlobNotFound and 404 to a not-found error and rethrows
everything else. A throttled or unauthorized account is an outage, and dressing
it as a 404 hides it behind a plausible answer.