```js
import { BlobServiceClient } from "@azure/storage-blob";
import { DefaultAzureCredential } from "@azure/identity";
const client = new BlobServiceClient(
"https://YOUR-ACCOUNT.blob.core.windows.net",
new DefaultAzureCredential()
);
const container = client.getContainerClient("[container]");
await container.createIfNotExists();
const blockBlob = container.getBlockBlobClient("[blob-name]");
await blockBlob.uploadFile("./file.bin");
```
Node-specific traps:
- **Data roles still apply.** Storage Blob Data Contributor on the storage account scope, same as every language. Contributor on the subscription is not enough.
- **Download streams.** `download()` returns a response whose `readableStreamBody` is a Node stream. Pipe it, do not treat it as a string. For small text blobs, `downloadToBuffer()` is simpler.
- **uploadFile vs uploadData.** `uploadFile` takes a path; `uploadData` takes a Buffer. Passing a path to `uploadData` silently uploads the path string as content.
- **Proxy env vars.** The JS SDK respects HTTP_PROXY/HTTPS_PROXY. In a VNet with forced tunneling and no proxy for Azure endpoints, stray proxy vars produce timeouts that look like auth failures. Check env before blaming RBAC.
- **Credential caching.** DefaultAzureCredential caches tokens; a 401 after a role change can be a cached token. Restart the process or wait out the token lifetime.
Verify: `container.getBlockBlobClient` then `exists()` on what you just uploaded. If exists() is false right after a successful upload, check you uploaded to the container you think you did (name typos are case-sensitive).