Search code examples
typescriptazureazure-blob-storagenext.js14

Next.js 14 App router download file with Azure Storage Blob - BlobServiceClient with Connection String


I had a hard time figuring out how to download from a Blob Storage with limited Access Levels using NextJs 14 with App Router. In my case it was because Typescript acted a bit bizzare when it came to return SearchParameters and instanciate a Response object with a readable stream.

Infact, while passing the readableStream to the Response constructor, like this:

    const blobStream = blobDownloadResponse.readableStreamBody;
    return new Response(blobStream as any, {

Typescript complained that

Argument of type 'ReadableStream | undefined' is not assignable to parameter of type 'BodyInit | null | undefined'. Type 'ReadableStream' is not assignable to type 'BodyInit | null | undefined'.

This is a lie, and simply having TS shut up with any solved the issue, and the download actually works:

import { BlobServiceClient } from "@azure/storage-blob";
import { NextResponse } from "next/server";

export async function GET(req: Request) {
  try {
    const filename = (req as any).nextUrl.searchParams.get('fileName')
    const connString = process.env.STORAGE_ACCOUNT_CONNSTRING
    const instance = BlobServiceClient.fromConnectionString(connString);
    const containerClient = instance.getContainerClient(process.env.ZIP_CONTAINER);
    const blobClient = containerClient.getBlobClient(filename!);
    const blobDownloadResponse = await blobClient.download();
    const blobStream = blobDownloadResponse.readableStreamBody;
    return new Response(blobStream as any, {
      headers: {
        "content-disposition": `attachment; filename="${filename}"`,
        "Content-Type": "application/zip"
      }
    })
  } catch (error) {
    console.error('Error:', error);
    return NextResponse.json(
      { error: 'error occurred while downloading the file.' },
      {status:500}
    )
  }
};

Solution

  • As stated by the Response Web Api Doc, the constructor can accept a ReadableStream, like the one returned by the BlobDownloadResponseParsed interface, yet Typescript came out with this error:

    Argument of type 'ReadableStream | undefined' is not assignable to parameter of type 'BodyInit | null | undefined'.
    Type 'ReadableStream' is not assignable to type 'BodyInit | null | undefined'.
    

    which led me to errors, misunderstanding and time waste.

    return new Response(blobStream as any, {
    

    did the trick for me. I know it's baaaaad to use any in TS (in that case one might as well just use simple JS instead) but i could not figure out another solution.

    I used as reference: