n4nAI

useChat hook tutorial: stream GPT-4o responses via n4n.ai

Hands-on tutorial: build a streaming chat UI with the useChat hook and GPT-4o via n4n.ai's OpenAI-compatible API using the Vercel AI SDK.

n4n Team2 min read504 words

Audio narration

Coming soon — every post will get a voice note here.

Building a responsive chat UI shouldn’t require wrestling with WebSocket plumbing. This hands-on tutorial implements a useChat hook GPT-4o streaming n4n.ai integration with the Vercel AI SDK, giving you token-by-token responses in a React component with under 50 lines of server code.

Prerequisites

  • Node.js 18.18 or later (App Router relies on modern async request APIs)
  • A Next.js 14+ project. If you don’t have one, scaffold it: npx create-next-app@latest demo --ts --app
  • An API key from the gateway (set as N4N_API_KEY in .env.local)
  • Basic familiarity with React hooks, TypeScript, and fetch

You do not need to install the official OpenAI SDK. The Vercel AI SDK speaks the OpenAI chat protocol against any compatible base URL.

Project setup

Install the required packages:

npm install ai @ai-sdk/openai @ai-sdk/react zod

@ai-sdk/openai provides the provider factory. @ai-sdk/react exposes useChat. ai ships streamText and the data stream protocol helpers.

Create .env.local at the project root:

N4N_API_KEY=sk-your-key-here

Your file tree should include:

app/
  api/chat/route.ts
  components/chat.tsx
  page.tsx

Server route: streaming with GPT-4o

Building the route handler

The route receives messages from the client and returns a streaming response. We point the OpenAI-compatible provider at the gateway endpoint.

// app/api/chat/route.ts
import { streamText } from 'ai';
import { createOpenAI } from '@ai-sdk/openai';

const n4nProvider = createOpenAI({
  baseURL: 'https://api.n4n.ai/v1',
  apiKey: process.env.N4N_API_KEY,
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = await streamText({
    model: n4nProvider('gpt-4o'),
    messages,
  });

  return result.toDataStreamResponse();
}

toDataStreamResponse() serializes the stream into the Vercel AI SDK data stream format. The useChat hook parses this format natively—no custom event listeners required.

Stream protocol details

The wire format is newline-delimited data: frames. A text delta looks like:

data: {"type":"text","value":"The"}
data: {"type":"text","value":" quick"}

The stream terminates with data: [DONE].

Expected output from curl

Verify the route before writing any UI:

curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Say hello in 5 words"}]}'

You should see incremental data: frames and a final [DONE]. If you get a 401, check the key. A 400 means malformed messages.

Client: useChat hook

Wire up the provider

Create a client component. The hook manages messages, input, submission, and loading state.

// app/components/chat.tsx
'use client';

import { useChat } from '@ai-sdk/react';

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading, stop } =
    useChat({ api: '/api/chat' });

  return (
    <div style={{ maxWidth: 600, margin: '2rem auto' }}>
      {messages.map((m) => (
        <div key={m.id} style={{ margin: '1rem 0' }}>
          <strong>{m.role}:</strong> {m.content}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Type a message…"
          style={{ width: '80%' }}
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
        {isLoading && (
          <button type="button" onClick={() => stop()} style={{ marginLeft: 8 }}>
            Stop
          </button>
        )}
      </form>
    </div>
  );
}

The chat component

Mount it in a page:

// app/page.tsx
import Chat from './components/chat';

export default function Page() {
  return (
    <main>
      <h1>GPT-4o Streaming Demo</h1>
      <Chat />
    </main>
  );
}

The useChat hook GPT-4o streaming pattern keeps the server stateless; the client sends the full messages array on every request. For prototypes and low-latency internal tools this is perfectly fine.

Running and verifying

Checkpoint: first message

Start the dev server:

npm run dev

Open http://localhost:3000. Type “Explain TCP fast open”. The assistant message renders incrementally:

user: Explain TCP fast open
assistant: TCP Fast Open (TFO) allows data to be sent in the initial SYN packet…

In the browser network tab, filter for chat and inspect the response. You’ll see the data: frames arriving every few milliseconds. React re-renders only the changing message node.

Adding a system prompt

Most real apps need a system message. Prepend it server-side so the client can’t mutate it:

// inside route.ts POST
const { messages } = await req.json();

const result = await streamText({
  model: n4nProvider('gpt-4o'),
  system: 'You are a concise senior network engineer.',
  messages,
});

Because the gateway is OpenAI-compatible, system maps to the standard top-level instruction field.

Extending the integration

Temperature and max tokens

Pass standard sampling params:

const result = await streamText({
  model: n4nProvider('gpt-4o'),
  messages,
  temperature: 0.2,
  maxTokens: 512,
});

Why this pattern scales

The client remains dumb. All model selection, key custody, and prompt engineering live in the route. Swapping gpt-4o for another model ID is a one-line change. The useChat hook handles reconnection, message IDs, and optimistic UI for free.

Production notes

Keep N4N_API_KEY server-side only. If you deploy to Vercel Edge, add export const runtime = 'edge' to route.ts for lower tail latency, but the Node runtime works identically because the gateway is a plain HTTPS API.

For long conversations, prune messages before calling streamText to control token spend. The gateway meters per token; you pay for exactly what streams.

Use stop() on the client to abort mid-stream; the route’s streamText honors the AbortSignal from the incoming request, so the upstream connection is cancelled promptly.

Tagsusechatgpt-4on4n-aistreaming

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All vercel ai sdk streaming chat ui (usechat) posts →