Backend · How to use a skill

build-ai-chatbot-api-route

Builds the server side of an AI chatbot - one API route that keeps the model key on the server, adds a system prompt, sends the chat history to an LLM such as OpenAI, Claude or Gemini, and streams the reply back. Use when the user wants to add a chatbot or AI assistant to their app, call the OpenAI API safely, or build a GPT-style chat backend.

Version 1.0 · Updated 2026-10-31

View raw SKILL.md

Install in Claude Code, Cursor or Codex (optional)

Only for AI coding tools that read skill folders. Run one line in a terminal, in your project folder. It downloads this skill's text files from TheSkillVault and nothing else; you can read them first with “View raw SKILL.md”.

Claude Code (all your projects)

curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/build-ai-chatbot-api-route/raw' -o ~/'.claude/skills/build-ai-chatbot-api-route/SKILL.md'

Cursor (this project)

curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/build-ai-chatbot-api-route/raw' -o '.cursor/skills/build-ai-chatbot-api-route/SKILL.md'

Codex and other Agent Skills tools (this project)

curl -fsSL --create-dirs 'https://theskillvault.ai/api/skills/build-ai-chatbot-api-route/raw' -o '.agents/skills/build-ai-chatbot-api-route/SKILL.md'

Or connect the whole library over MCP

Add TheSkillVault to Cursor or add this to your app's MCP settings (read-only; help):

{
  "mcpServers": {
    "theskillvault": {
      "url": "https://theskillvault.ai/mcp"
    }
  }
}

Build an AI chatbot API route

The browser never talks to the AI provider directly. It calls your route, and your route holds the key.

When to use

  • Adding a chatbot, support assistant or "ask AI" box to an app.
  • You have an API key and need a safe place to use it.
  • Your current code calls the AI provider from the browser (move it).

Steps

  1. Get a key and keep it secret. Put OPENAI_API_KEY (or your provider's key) in .env.local. Add it to your host's environment settings too. Never prefix it with NEXT_PUBLIC_.
  2. Install the SDK. For example npm i openai. Pick the model in an env var such as OPENAI_MODEL, so you can change it without a code change.
  3. Create the route. In Next.js App Router, add app/api/chat/route.ts with a POST handler. Other frameworks use any POST endpoint.
  4. Validate the input. Accept { messages: [{ role, content }] } only. Keep the last 20 messages and reject huge inputs (for example over 8,000 characters).
  5. Add a system prompt on the server. Say what the bot is for, what it must refuse, and the tone. Users must not be able to replace it.
  6. Call the model with streaming on and return the text as it arrives (see the example).
  7. Handle errors. Return a friendly message on timeouts or provider errors, and log the real error on the server only.
  8. Add limits before launch. Use the add-chatbot-guardrails-and-rate-limits skill.

Example (Next.js + OpenAI)

// app/api/chat/route.ts
import OpenAI from "openai";
const client = new OpenAI(); // reads OPENAI_API_KEY
const SYSTEM = "You are the helpful assistant for Acme. Only answer questions about Acme's product.";

export async function POST(req: Request) {
  const { messages } = await req.json();
  if (!Array.isArray(messages)) return new Response("Bad request", { status: 400 });
  const history = messages.slice(-20).map((m: any) => ({ role: m.role === "assistant" ? "assistant" : "user", content: String(m.content).slice(0, 8000) }));
  const stream = await client.responses.create({ model: process.env.OPENAI_MODEL!, instructions: SYSTEM, input: history, stream: true });
  const body = new ReadableStream({
    async start(c) {
      const enc = new TextEncoder();
      for await (const ev of stream) if (ev.type === "response.output_text.delta") c.enqueue(enc.encode(ev.delta));
      c.close();
    },
  });
  return new Response(body, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
}

Gotchas

  • A key in browser code is public. Anyone can copy it and run up your bill.
  • Only accept "user" and "assistant" roles from the client. A client-sent "system" message can override your rules.
  • Serverless functions have time limits. Streaming keeps long answers from timing out.

Works with any stack

The same shape works with Claude, Gemini or a local model, and with Express, Hono, Remix or SvelteKit. If you use the Vercel AI SDK, streamText does steps 6 and 7 for you. Check its current docs for the exact helpers.

Done when

  • The key exists only in server environment variables, and searching the built site for it finds nothing.
  • curl -N -X POST localhost:3000/api/chat -d '{"messages":[{"role":"user","content":"hi"}]}' streams a reply.
  • Bad input gets a 400 response, and provider errors show a friendly message.

Example prompt

"Add an AI chatbot to my Next.js site that answers questions about my product, without exposing my OpenAI key."

Licence: CC BY 4.0. Attribution when you reuse it: From TheSkillVault, https://theskillvault.ai, CC BY 4.0