Problem:
I have a server-sent-events route on my nextjs application which is running locally. This route is in this path: (using App Router)
‘src/app/api/ai/embeddings/generate/route.ts’
I’m creating a new EventSource on the frontend as follows:
const eventSource = new EventSource("/api/ai/embeddings/generate");
For a long time this was working fine, suddenly, without any change to how the requests are made or received, the route started returning 404 when requested.
The only changes made that day were to a function inside this route. However I can confirm that it wasn’t the issue as I used a previous commit to revert the changes, yet the issue persisted.
Attempted Solutions:
At first I tried console logging at the top of the route, however I never saw the log in my terminal (I also double checked the browser console, no logs either).
I then moved on to reverting any changes made to that route and testing again, however the issue persisted.
When I create a dumb route with just a response, something like this:
import { NextResponse } from "next/server";
export async function GET() {
return new Response("/api/ai/embeddings/generate route working!");
}
and then go to that url in the browser, I can see the message. (sometimes I didn’t see it, but since I didn’t find the cause I assume it’s just the .next local server not updating quick enough)
Code:
Below is the relevant code:
Backend
export async function GET() {
try {
const user = await authorized();
if (!user) return NextResponse.json({ msg: "Unauthorized" }, { status: 401 });
const supabase = await createClient();
const embeddingStatus: Record<Enums<"embedding">, boolean> = { website: false, file: false, text: false, qa: false, notion: false };
const stream = new ReadableStream({
async start(controller) {
const pushUpdate = (data: { type: any; status: string; error?: string }) => {
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(data)}\n\n`));
};
const handleEmbeddingGeneration = async (
type: Enums<"embedding">,
generatorFunction: (supabase: SupabaseClient, user: User) => Promise<true | null>
) => {
try {
const embeddings: true | null = await generatorFunction(supabase, user);
embeddingStatus[type] = embeddings ? true : false;
pushUpdate({ type, status: embeddings ? "completed" : "skipped" });
} catch (err: unknown) {
const error = err as { message: string };
embeddingStatus[type] = false;
pushUpdate({ type, status: "error", error: error.message });
}
};
await Promise.all([
handleEmbeddingGeneration("website", generate_website_embed),
handleEmbeddingGeneration("file", generate_file_embed),
handleEmbeddingGeneration("text", generate_text_embed),
handleEmbeddingGeneration("qa", generate_qa_embed),
handleEmbeddingGeneration("notion", generate_notion_embed),
]);
const { data, error } = await supabase.from("embedding_history").insert({
embeddings: embeddingStatus,
user: user.id,
});
if (error) console.error(error);
controller.close();
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
});
} catch (err) {
console.error(err);
return NextResponse.json({ msg: "Unexpected error occurred" }, { status: 500 });
}
}
Frontend
async function generateEmbeddings(e: React.MouseEvent<HTMLButtonElement>) {
e.preventDefault();
try {
setShowEmbeddingMessage(true);
const eventSource = new EventSource("/api/ai/embeddings/generate");
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data);
setEmbeddingStatus((prev) => ({
...prev,
[data.type]: data.status === "completed",
}));
};
eventSource.onerror = () => {
eventSource.close();
dispatch(
addToast({
type: "error",
description: "Unexpected error occurred.",
})
);
setShowEmbeddingMessage(false);
};
} catch (err: unknown) {
const error = err as { msg: string };
if (error) console.error(error);
dispatch(
addToast({
type: "error",
description: error.msg || "An unexpected error occurred.",
})
);
setShowEmbeddingMessage(false);
}
}
Project Information:
next: 15.1.7