AI Chat
AI chat with the Vercel AI Gateway in the Nuxt template.
The template includes an AI chat Example with a streaming Vue interface and
a Nitro endpoint backed by Vercel AI Gateway. The server currently uses the
fixed model openai/gpt-5.2.
What it provides
- A chat page at
/ai/chatand a reusableChatBot.vuecomponent. - AI Elements for conversations, messages, prompt input, attachments, reasoning, sources, loading, copy, and regenerate actions.
- Streaming UI messages through the AI SDK's
Chatclass from@ai-sdk/vue. - A lazy-initialized AI Gateway client and streamed Nitro response.
- Authentication by default, an explicit public-demo switch, and Zod request validation.
Important files
| File | Role |
|---|---|
app/pages/(marketing)/ai/chat.vue | Chat page shell |
app/components/ai/ChatBot.vue | Chat state, prompt controls, and message rendering |
app/components/ai-elements/* | Reusable chat interface primitives |
server/api/chat.ts | Validated Gateway request and stream response |
server/utils/ai-auth.ts | requireAiAuth demo/auth policy |
nuxt.config.ts | aiGatewayApiKey and public.aiPublicDemo runtime config |
Setup
Set the Gateway key as a server-only variable:
NUXT_AI_GATEWAY_API_KEY=your-ai-gateway-api-keyRestart pnpm dev after changing .env. The key must not use a
NUXT_PUBLIC_ prefix.
Request and streaming flow
ChatBot.vue creates new Chat({}), sends UI messages to /api/chat, and
renders each streamed message part. Sources, reasoning, text, submitted state,
attachments, copy, and regenerate controls already have UI components.
server/api/chat.ts uses defineLazyEventHandler so the Gateway client is
created once after first use. Each request then:
- calls
requireAiAuth(event); - parses a non-empty
messagesarray with Zod; - converts UI messages with
convertToModelMessages; - calls
streamTextwithgateway("openai/gpt-5.2"); and - returns
toUIMessageStreamResponse({ sendSources: true, sendReasoning: true }).
The UI can display source and reasoning parts when the selected model/provider actually emits them.
Current control limitations
The model dropdown and Search toggle are currently UI starters. The client
sends model and webSearch, but the server schema only reads messages and
always calls openai/gpt-5.2; no search tool is configured. Selecting
DeepSeek or toggling Search therefore does not change backend behavior.
For a single model, update the fixed Gateway model in server/api/chat.ts and
remove misleading controls. For user-selectable models, add a Zod enum on the
server and map it through a small allowlist. Do not pass an arbitrary client
model identifier directly to a paid provider. Search requires an actual tool or
provider option plus handling for its source parts.
Access control
requireAiAuth requires a session unless the following variable is exactly
true:
NUXT_PUBLIC_AI_PUBLIC_DEMO=trueThis switch is intentionally public because it changes client-visible demo
behavior. Leave it unset or false in production. Authentication and request
shape validation are integrated, but they do not control how much an
authenticated account can spend.
Production hardening
Before exposing chat to real users, add:
- per-user and per-plan usage quotas;
- token/output limits and model-specific cost caps;
- prompt/output moderation appropriate to the product;
- a server-side model allowlist and removal/wiring of UI-only controls;
- structured usage, latency, provider-error, and cost logging without storing sensitive prompts by default;
- cancellation/timeouts and user-facing error states; and
- rate limiting as a recommended add-on (it is not shipped by the template).
Verify
- Missing
NUXT_AI_GATEWAY_API_KEYfails clearly in server logs. - Logged-out requests return 401 when public demo is disabled.
- A logged-in prompt streams text and reasoning/source UI does not crash.
- Empty or malformed
messagesreturns a 400-class validation response. - The deployed model and visible controls agree.
- Quotas and cost alerts stop excessive paid usage before launch.
