Azure Blob Driver β
Azure Blob Storage driver for @minimajs/disk. Use web-native File APIs to interact with Azure Blob Storageβno need to learn the Azure SDK.
Features β
- π Web-Native APIs - Use File, Blob, ReadableStream instead of Azure SDK methods
- β Streaming Uploads - Efficient streaming uploads to Azure Blob Storage
- β Streaming Downloads - Direct streaming from Azure without buffering
- β CDN Integration - Configure Azure CDN or custom public URLs
- β Metadata Support - Store and retrieve custom blob metadata
- β
Server-Side Copy - Fast native Azure copy operations via
beginCopyFromURL - β List Operations - Paginated blob listing with prefix support
- β Multi-Container - Work across multiple containers using full Azure URLs
- β
Minimal Dependencies - Only requires
@azure/storage-blob
Installation β
npm install @minimajs/azure-blob @minimajs/disk
# or
bun add @minimajs/azure-blob @minimajs/diskUsage β
Basic Configuration β
import { createAzureBlobDriver } from "@minimajs/azure-blob";
import { createDisk } from "@minimajs/disk";
const disk = createDisk({
driver: createAzureBlobDriver({
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING!,
container: "uploads",
}),
});
// Web-native API - works like browser File API
const file = new File(["Hello World"], "hello.txt");
await disk.put(file); // Auto-generates unique filename
// Or specify path
await disk.put("avatars/photo.jpg", imageBuffer);
// Retrieve file - standard File methods
const retrieved = await disk.get("avatars/photo.jpg");
if (retrieved) {
const buffer = await retrieved.arrayBuffer(); // Standard File.arrayBuffer()
const text = await retrieved.text(); // Standard File.text()
const stream = retrieved.stream(); // Standard File.stream()
}
// Delete file
await disk.delete("avatars/photo.jpg");With CDN URL β
const disk = createDisk({
driver: createAzureBlobDriver({
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING!,
container: "uploads",
publicUrl: "https://mycdn.azureedge.net", // Azure CDN endpoint
}),
});
await disk.put("images/logo.png", logoBuffer);
// Get CDN URL
const url = await disk.url("images/logo.png");
console.log(url); // https://mycdn.azureedge.net/images/logo.pngDirect Azure Blob URL (without CDN) β
When no publicUrl is set, disk.url() returns the direct Azure Blob Storage URL:
const disk = createDisk({
driver: createAzureBlobDriver({
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING!,
container: "uploads",
}),
});
const url = await disk.url("images/logo.png");
console.log(url);
// https://<account>.blob.core.windows.net/uploads/images/logo.pngMulti-Container Setup β
When container is NOT set in the driver, use full Azure Blob URLs:
const disk = createDisk({
driver: createAzureBlobDriver({
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING!,
// No container specified
}),
});
// Specify container in the full URL
await disk.put("https://<account>.blob.core.windows.net/images/logo.png", logoBuffer);
await disk.put("https://<account>.blob.core.windows.net/documents/report.pdf", pdfBuffer);
const file = await disk.get("https://<account>.blob.core.windows.net/images/logo.png");Using an Existing BlobServiceClient β
If you already have a BlobServiceClient instance (e.g., with custom authentication), pass it directly:
import { BlobServiceClient } from "@azure/storage-blob";
import { createAzureBlobDriver } from "@minimajs/azure-blob";
import { createDisk } from "@minimajs/disk";
const client = new BlobServiceClient(
`https://${process.env.AZURE_STORAGE_ACCOUNT}.blob.core.windows.net`,
credential // DefaultAzureCredential, SAS token, etc.
);
const disk = createDisk({
driver: createAzureBlobDriver(client, {
container: "uploads",
publicUrl: "https://mycdn.azureedge.net",
}),
});Configuration β
interface AzureBlobDriverOptions {
/**
* Azure Storage connection string.
* Required when not passing a BlobServiceClient directly.
*/
connectionString: string;
/**
* Default container name.
* Optional - can use full Azure Blob URLs instead.
*/
container?: string;
/**
* Public URL for serving files (e.g., Azure CDN endpoint).
* When set, disk.url() returns URLs with this prefix.
*/
publicUrl?: string;
}Examples β
Upload with Metadata β
await disk.put("documents/report.pdf", pdfBuffer, {
type: "application/pdf",
metadata: {
author: "Jane Doe",
department: "Finance",
year: "2024",
},
});
// Retrieve with metadata
const file = await disk.get("documents/report.pdf");
console.log(file.type); // "application/pdf"
console.log(file.metadata); // { author: "Jane Doe", ... }Update Metadata β
The AzureBlobDriver supports updating metadata and content type without re-uploading:
import { AzureBlobDriver } from "@minimajs/azure-blob";
const driver = createAzureBlobDriver({ ... }) as AzureBlobDriver;
await driver.updateMetadata("documents/report.pdf", {
type: "application/pdf",
metadata: { reviewed: "true", reviewedBy: "admin" },
});List Blobs β
// List all blobs in the container
for await (const file of disk.list()) {
console.log(file.href, file.size);
}
// List with prefix
for await (const file of disk.list("uploads/2024/")) {
console.log(file.name, file.type, file.lastModified);
}
// List with limit
for await (const file of disk.list("uploads/", { limit: 50 })) {
console.log(file.href);
}Copy and Move β
// Copy within same container
await disk.copy("originals/photo.jpg", "thumbnails/photo.jpg");
// Move (copy + delete source)
await disk.move("uploads/temp/file.jpg", "uploads/processed/file.jpg");
// Copy using DiskFile reference
const source = await disk.get("originals/photo.jpg");
await disk.copy(source, "backups/photo.jpg");Streaming Large Files β
// Upload a large file from a ReadableStream
const stream = fs.createReadStream("large-video.mp4");
await disk.put("videos/large-video.mp4", Readable.toWeb(stream));
// Stream download to HTTP response
const file = await disk.get("videos/large-video.mp4");
return new Response(file.stream(), {
headers: {
"Content-Type": file.type,
"Content-Length": String(file.size),
},
});Concurrent Operations β
// Upload multiple files in parallel
await Promise.all([
disk.put("file1.txt", "Data 1"),
disk.put("file2.txt", "Data 2"),
disk.put("file3.txt", "Data 3"),
]);
// Download multiple files in parallel
const [file1, file2] = await Promise.all([
disk.get("file1.txt"),
disk.get("file2.txt"),
]);Azure Credentials β
Connection String (simplest) β
AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net"const disk = createDisk({
driver: createAzureBlobDriver({
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING!,
container: "uploads",
}),
});Managed Identity / DefaultAzureCredential β
For production deployments on Azure (App Service, AKS, Azure Functions), use DefaultAzureCredential for passwordless auth:
npm install @azure/identityimport { BlobServiceClient } from "@azure/storage-blob";
import { DefaultAzureCredential } from "@azure/identity";
import { createAzureBlobDriver } from "@minimajs/azure-blob";
import { createDisk } from "@minimajs/disk";
const client = new BlobServiceClient(
`https://${process.env.AZURE_STORAGE_ACCOUNT}.blob.core.windows.net`,
new DefaultAzureCredential()
);
const disk = createDisk({
driver: createAzureBlobDriver(client, { container: "uploads" }),
});SAS Token β
import { BlobServiceClient } from "@azure/storage-blob";
const client = new BlobServiceClient(
`https://${process.env.AZURE_STORAGE_ACCOUNT}.blob.core.windows.net?${process.env.AZURE_SAS_TOKEN}`
);
const disk = createDisk({
driver: createAzureBlobDriver(client, { container: "uploads" }),
});href Format β
Azure Blob Storage hrefs follow the standard Azure URL format:
https://<account>.blob.core.windows.net/<container>/<blob-path>When publicUrl is configured and disk.url() is called, the returned URL uses the CDN prefix instead:
https://<cdn>.azureedge.net/<blob-path>All disk operations (get, copy, move, delete) accept both the full Azure URL and plain paths (when container is configured):
// These are equivalent when container: "uploads" is set
await disk.get("avatars/photo.jpg");
await disk.get("https://<account>.blob.core.windows.net/uploads/avatars/photo.jpg");Error Handling β
import { DiskFileNotFoundError, DiskWriteError, DiskReadError } from "@minimajs/disk";
try {
const file = await disk.get("missing.txt");
} catch (error) {
if (error instanceof DiskFileNotFoundError) {
console.log("Blob not found:", error.href);
} else if (error instanceof DiskReadError) {
console.log("Failed to read blob:", error.message);
}
}
try {
await disk.put("uploads/photo.jpg", imageBuffer);
} catch (error) {
if (error instanceof DiskWriteError) {
console.log("Failed to upload blob:", error.message);
}
}Related Documentation β
- Core Disk Package - Main documentation
- AWS S3 Driver - Amazon S3 storage
- Protocol Disk - Multi-cloud routing
- Filesystem Driver - Local filesystem storage
Key Benefits β
β Web-Native APIs - Use familiar File, Blob, ReadableStream APIs β No Azure SDK Learning Curve - Forget complex SDK methods β Provider Agnostic - Same code works with filesystem, S3, etc. β Type-Safe - Full TypeScript support β Streaming - Efficient large file handling β Multi-Cloud - Easy to switch or combine providers