Next JS (Edge Runtime)
Overview
Section titled “Overview”Use this example to send chat data from Next.js Edge Function to Growl.
Examples
Section titled “Examples”Streaming Text
Section titled “Streaming Text”In this example, we use the Next.js Edge Runtime and forward the data to Growl.
We are forwarding the following data in this example:
- User data - user identifiers like email to track across sessions
- Request headers - to track events across users and sessions
- Request data - to be stored and analyzed by Growl
- Response data - to be stored and analyzed by Growl
import recordGrowlEvent from "./growl";export const config = { runtime: "edge" };
export default async function handler(req: Request) { // visitor_id is required - must be obtained from window.GrowlAds.getVisitorId() on client-side const { messages, visitor_id }: { messages: any[]; visitor_id: string } = await req.json();
const upstream = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, }, body: JSON.stringify({ model: "gpt-4o-mini", messages, stream: true }), });
if (!upstream.ok) { return new Response("Error from OpenAI", { status: upstream.status }); }
const encoder = new TextEncoder(); const decoder = new TextDecoder(); const summary = { textParts: [] as string[], fullText: "" };
const readable = new ReadableStream({ async start(controller) { const reader = upstream.body!.getReader();
try { while (true) { const { done, value } = await reader.read(); if (done) break;
const chunk = decoder.decode(value); const lines = chunk.split("\n");
for (const line of lines) { if (line.startsWith("data: ")) { const data = line.slice(6); if (data === "[DONE]") { summary.fullText = summary.textParts.join(""); const headersObject = Object.fromEntries(req.headers.entries()); try { await recordGrowlEvent({ publisher_id: "<publisher_id>", user_id: "<user-id>", user_email: "<user-email>", visitor_id: visitor_id, // from client: await GrowlAds.getVisitorId() chat_id: "<chat-id>", headers: headersObject, user_message: { text : messages[messages.length -1] }, ai_message: { text: summary.fullText } }) } catch (e) { // ignore analytics errors } controller.close(); return; }
try { const parsed = JSON.parse(data); const content = parsed.choices?.[0]?.delta?.content; if (content) { controller.enqueue(encoder.encode(content)); summary.textParts.push(content); } } catch (e) { // Skip malformed JSON } } } } } catch (error) { controller.error(error); } finally { reader.releaseLock(); } }, });
return new Response(readable, { headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-cache, no-transform", Connection: "keep-alive", }, });}Non-streaming Text
Section titled “Non-streaming Text”import recordGrowlEvent from "./growl";export const config = { runtime: "edge" };
export default async function handler(req: Request) { // visitor_id is required - must be obtained from window.GrowlAds.getVisitorId() on client-side const { messages, visitor_id }: { messages: any[]; visitor_id: string } = await req.json();
const upstream = await fetch("https://api.openai.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, }, body: JSON.stringify({ model: "gpt-4o-mini", messages, stream: false }), });
if (!upstream.ok) { return new Response("Error from OpenAI", { status: upstream.status }); }
const responseData = await upstream.json(); const fullText = responseData.choices?.[0]?.message?.content || "";
// Send analytics event to Growl const headersObject = Object.fromEntries(req.headers.entries()); try { await recordGrowlEvent({ publisher_id: "<publisher_id>", user_id: "<user-id>", user_email: "<user-email>", visitor_id: visitor_id, // from client: await GrowlAds.getVisitorId() chat_id: "<chat-id>", headers: headersObject, user_message: { text: messages[messages.length - 1] }, ai_message: { text: fullText }, }); } catch (e) { // ignore analytics errors }
return new Response(JSON.stringify({ content: fullText }), { headers: { "Content-Type": "application/json", }, });}