In my Next.js 14 app, I have a /research/page.tsx
file where I am receiving a file as user input and sending it to a server-side API for further processing. Here’s the method that does this:
const handleFileDrop = async (file: File) => {
// Convert file to blob
const arrayBuffer = await file.arrayBuffer()
const blob = new Blob([arrayBuffer])
const paperId = await generateFileId(arrayBuffer)
const paperTitle = file.name
// Send file to api/upload for upload to S3
try {
const res = await fetch("/api/upload", {
method: "POST",
body: file,
})
if (!res.ok) throw new Error(await res.text())
console.log(await res.json())
} catch (error: any) {
// handle error
}
}
On the server, the API at /api/upload/route.ts is meant to do a bunch of things with the file and once done, redirect the user to a new dynamically generated route:
/* eslint-disable import/prefer-default-export, @typescript-eslint/no-unused-vars */
import { NextRequest, NextResponse } from "next/server"
import generateFileId from "@/lib/generate-file-id"
import { PDFLoader } from "langchain/document_loaders/fs/pdf"
// import AWS from "aws-sdk"
// const s3 = new AWS.S3()
async function streamToArrayBuffer(stream: ReadableStream) {
return new Uint8Array(await new Response(stream).arrayBuffer())
}
export async function POST(request: NextRequest) {
const data = await request.body
if (!data) return NextResponse.json({ success: false })
const arrayBuffer = await streamToArrayBuffer(data)
const blob = new Blob([arrayBuffer])
const loader = new PDFLoader(blob)
const pageLevelDocs = await loader.load()
const pageCount = pageLevelDocs.length
const paperId = await generateFileId(arrayBuffer)
/*
STEP 1: Upload file to S3
STEP 2: Add reference to file in Postgres
*/
console.log("test")
return NextResponse.redirect(new URL(`/research/${paperId}`, request.url))
}
Everything works fine down to the console.log("test")
statement. But the redirect wouldn’t work. The browser stays on /research
instead of redirecting to /research/[paperId]
.
I even tried redirecting from the client upon a successful response from the API, using useRouter
and router.push()
but even that wouldn’t work:
const handleFileDrop = async (file: File) => {
// Convert file to blob
const arrayBuffer = await file.arrayBuffer()
const blob = new Blob([arrayBuffer])
const paperId = await generateFileId(arrayBuffer)
const paperTitle = file.name
// Send file to api/upload for upload to S3
try {
const res = await fetch("/api/upload", {
method: "POST",
body: file,
})
if (!res.ok) throw new Error(await res.text())
console.log(await res.json())
const router = useRouter()
router.push("/research/${paperId}")
} catch (error: any) {
// handle error
}
}
The browser remains on the originating URL.
I have two questions:
What is missing in my code, and
What is the conventional approach to this situation? I need the new page to be able to access the file
object too.
You cannot redirect a network call made via JavaScript like you did. Instead, you would return a normal response:
import { NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const data = await request.body;
if (!data) return NextResponse.json({ success: false });
const arrayBuffer = await streamToArrayBuffer(data);
const blob = new Blob([arrayBuffer]);
const loader = new PDFLoader(blob);
const pageLevelDocs = await loader.load();
const pageCount = pageLevelDocs.length;
const paperId = await generateFileId(arrayBuffer);
/*
STEP 1: Upload file to S3
STEP 2: Add reference to file in Postgres
*/
console.log("test");
return NextResponse.json({
success: true,
paperId: paperId,
});
}
And handle the redirection on the client:
// In the component body:
const router = useRouter();
const handleFileDrop = async (file: File) => {
// Convert file to blob
const arrayBuffer = await file.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const paperId = await generateFileId(arrayBuffer);
const paperTitle = file.name;
// Send file to api/upload for upload to S3
try {
const res = await fetch("/api/upload", {
method: "POST",
body: file,
});
if (!res.ok) throw new Error(await res.text());
const { paperId } = await res.json();
router.push(`/research/${paperId}`);
} catch (error: any) {
// handle error
}
};
At this point, if you have a research/[paperId]/page.tsx
, you are good to go.