--- name: integrating-glirastes-nextjs description: Integrating the Glirastes SDK into a Next.js App Router project — chat route handler, VercelAiChat widget, co-located ai-tool.ts files, codegen pipeline. Use when adding AI chat to a Next.js app where Next.js itself is the backend. version: 1.0.0 tags: - glirastes - nextjs - ai - app-router triggers: - integrate glirastes - integrate glirastes nextjs - add glirastes to next - add chat to next.js - createAiChatHandler - VercelAiChat - chat route nextjs - ai chat next.js --- # Integrating Glirastes into Next.js > **Scope:** Next.js is the only backend in this project. If a separate NestJS / Express backend exists, use [integrating-glirastes-nestjs](../integrating-glirastes-nestjs/SKILL.md) instead — putting the chat route on the real backend avoids duplicate auth and an extra HTTP hop per tool call. > > **Companion skills:** > - [maintaining-glirastes-tools](../maintaining-glirastes-tools/SKILL.md) — tool authoring deep-dive, all CLI commands, codegen workflow > - [building-glirastes-chat-ui](../building-glirastes-chat-ui/SKILL.md) — chat UI customization, mentions, voice input, action bus ## Stack detection Confirm before applying: - `next.config.{ts,js,mjs}` exists at the project root → Next.js ✅ - No `nest-cli.json` or sibling backend workspace - `package.json` shows React 19+, Next.js 15+ If a NestJS backend lives in the same workspace (e.g. `apps/api/`), stop and use the NestJS skill — the chat route belongs there. ## Step 1 — Install ```bash # Core + a model provider npm install glirastes @ai-sdk/openai # React UI peers (declared as optional peerDeps in glirastes 0.3.1+) npm install @ai-sdk/react react-markdown # Optional: voice input with live waveform npm install wavesurfer.js ``` `@ai-sdk/openai` is the OpenAI provider. Swap for `@ai-sdk/anthropic`, `@ai-sdk/mistral`, etc. — Glirastes accepts any AI SDK v6 provider. ## Step 2 — Environment variables Add to `.env.local`: ``` OPENAI_API_KEY=sk-... # Optional — unlocks Glirastes platform features (PII Shield, intent routing, telemetry) # GLIRASTES_API_KEY=glir_... ``` API keys must stay server-side. Next.js Route Handlers run on the server, so this is safe — never reference `OPENAI_API_KEY` from a client component. ## Step 3 — File structure convention Co-locate `ai-tool.ts` next to the route it wraps. The codegen scanner discovers tools by this convention: ``` src/app/api/ ├── chat/route.ts # createAiChatHandler with the registry ├── tasks/ │ ├── route.ts # The actual API handler │ └── ai-tool.ts # AI-callable wrapper (defineEndpointTool) ├── tasks/[id]/ │ ├── route.ts │ └── ai-tool.ts └── users/search/ ├── route.ts └── ai-tool.ts src/components/ ├── tasks/ai-ui-tool.ts # UI tool definitions (defineUiTool) └── layout/ai-ui-tool.ts src/generated/ai-tools/ ├── endpoint.generated.ts # AUTO — produced by `glirastes generate-tools` ├── ui.generated.ts ├── modules.generated.ts └── action-ids.generated.ts ``` Ignore the `src/generated/ai-tools/` directory in your editor — it's regenerated by the CLI on every `predev` / `prebuild`. ## Step 4 — Bootstrap your first tool Use the CLI scaffolder. Pass an existing route file: ```bash npx glirastes scaffold --route src/app/api/tasks/route.ts # Add --dry-run to preview without writing # Add --force to overwrite existing ai-tool.ts ``` The scaffolder analyzes the route (HTTP methods, path params, body fields, search params) and emits a starter `src/app/api/tasks/ai-tool.ts`: ```ts import { z } from 'zod'; import { defineEndpointTool } from 'glirastes'; export const aiTool = defineEndpointTool({ id: 'tasks.list', toolName: 'list_tasks', module: 'task_query', description: 'list tasks. TODO: write a clear description for the AI.', method: 'GET', path: '/api/tasks', inputSchema: z.object({ status: z.enum(['open', 'done']).optional(), }), // uiPattern: { type: 'filter-and-navigate', target: 'tasks', filterMapping: { status: 'status' } }, }); ``` Edit `description` (the LLM reads this to decide when to call the tool), refine the schema, optionally add a `uiPattern`. For the full tool authoring API, see [maintaining-glirastes-tools](../maintaining-glirastes-tools/SKILL.md). ## Step 5 — Wire the chat route handler Two paths depending on tool count: ### Few tools (≤5) — manual import ```ts // src/app/api/chat/route.ts import { openai } from '@ai-sdk/openai'; import { endpointToolsToRegistry } from 'glirastes/server'; import { createAiChatHandler } from 'glirastes/server/nextjs'; import { aiTool as listTasks } from '../tasks/ai-tool'; export const POST = createAiChatHandler({ tools: endpointToolsToRegistry([listTasks]), model: openai('gpt-4o-mini'), systemPrompt: 'You are a helpful assistant for our app.', }); ``` OK as a starting point. The moment you go past a handful of tools, switch to the codegen pipeline below — manual maintenance of the import list breaks down fast. ### Production: codegen pipeline (required for non-trivial apps) In Glirastes 0.3.x **the canonical Next.js setup is to wire the codegen as a build hook**. Add to `package.json`: ```json { "scripts": { "predev": "glirastes generate-tools --quiet", "prebuild": "glirastes generate-tools --quiet", "ai:scaffold": "glirastes scaffold", "ai:coverage": "glirastes coverage", "ai:coverage:ci": "glirastes coverage --ci", "ai:validate": "glirastes validate-tools", "ai:validate:ci": "glirastes validate-tools --ci" } } ``` The `predev` and `prebuild` hooks regenerate the registry on every dev server start and CI build, so the generated files stay in sync with your `*.ai-tool.ts` source-of-truth. Without these hooks the chat route imports would point at stale generated output. > **Coming in 0.4.0:** `buildToolRegistry` based on `import.meta.glob` / `require.context` will let you skip the codegen step and the build hook entirely. Until then, the codegen pipeline is the supported pattern for any non-trivial Next.js app. Then import the generated registry instead of individual tools: ```ts // src/app/api/chat/route.ts import { openai } from '@ai-sdk/openai'; import { createAiChatHandler } from 'glirastes/server/nextjs'; import { endpointToolRegistry } from '@/generated/ai-tools/endpoint.generated'; import { uiToolRegistry } from '@/generated/ai-tools/ui.generated'; export const POST = createAiChatHandler({ tools: { ...endpointToolRegistry, ...uiToolRegistry }, model: openai('gpt-4o-mini'), systemPrompt: 'You are a helpful assistant for our app.', }); ``` The registries are typed as `ToolRegistry` (Record) — pass them straight to `createAiChatHandler({ tools })`. ## Step 6 — Mount the chat widget Client-side component, then drop into the root layout: ```tsx // src/components/chat/ChatWidget.tsx 'use client'; import { VercelAiChat } from 'glirastes/react/vercel'; export function ChatWidget() { return ; } ``` ```tsx // src/app/layout.tsx import { ChatWidget } from '@/components/chat/ChatWidget'; import 'glirastes/react/styles.css'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` For UI customization (props on `VercelAiChat`, theming, mentions, voice input, custom approval cards), see [building-glirastes-chat-ui](../building-glirastes-chat-ui/SKILL.md). ## Step 7 — Verify Run the codegen + production build first — surfaces most issues: ```bash npm run ai:validate # catches malformed ai-tool.ts files npm run ai:coverage # lists routes without ai-tool.ts (informational) npm run build # full Next.js build, runs `predev`/`prebuild` scripts ``` Build output should list `/api/chat` as a dynamic route (`ƒ /api/chat`). Then: ```bash npm run dev ``` Open the app, click the floating chat trigger (bottom-right by default), send a message that should fire your tool (*"show me my open tasks"*). The tool result must show up; `uiPattern` actions fire automatically (e.g. `filter-and-navigate` rewrites the URL). ## Step 8 — Hook up frontend UI actions (optional) If your tools declare `uiPattern: { type: 'open-dialog' | 'open-detail' | 'refresh' | 'toast' }`, the SDK derives an action ID and fires it through the `UiActionBus`. Register handlers anywhere a relevant component mounts: ```tsx 'use client'; import { useAiClientAction } from 'glirastes/react'; import { useRouter } from 'next/navigation'; export function TaskList() { const router = useRouter(); useAiClientAction('task-details.open', (payload) => { router.push(`/tasks/${payload?.taskId}`); }); // ... } ``` Action IDs are derived from `uiPattern`: | `uiPattern.type` | Action ID | |---|---| | `open-detail`, `entity: 'task'` | `task-details.open` | | `open-dialog`, `dialog: 'create-task'` | `task-create-dialog.open` | | `refresh`, `target: 'tasks'` | `tasks.refresh` | | `toast` | dispatched directly via your toast lib (sonner/react-hot-toast) | `glirastes coverage` reports any UI action ID declared in tools but not handled in `.tsx` files (and vice versa). ## CLI quick reference | Command | Purpose | Common flags | |---|---|---| | `glirastes scaffold --route ` | Create `ai-tool.ts` next to a `route.ts` | `--dry-run`, `--force` | | `glirastes generate-tools` | Scan all `*.ai-tool.ts` files, emit `src/generated/ai-tools/*.generated.ts` | `--quiet` (predev/prebuild) | | `glirastes generate-endpoint-tools` | Endpoint registry only | `--quiet` | | `glirastes generate-ui-tools` | UI tool registry + action IDs | `--quiet` | | `glirastes generate-modules` | Module/intent registry | `--meta @/lib/ai/module-meta` | | `glirastes generate-auto` | Auto-detect repo layout, run the right generator | — | | `glirastes validate-tools` | Validate naming, duplicates, missing approvals | `--ci`, `--fix`, `--quiet` | | `glirastes coverage` | Report routes without `ai-tool.ts`, orphan handlers | `--ci`, `--quiet` | | `glirastes test` | Scaffold an AI behavior test file | — | | `glirastes generate-skills` | Export current tool registry as Claude Code / Codex skills (for *agents to call your tools*) | `--app-name`, `--base-url`, `--auth bearer` | | `glirastes generate-mcp-server` | Export current tool registry as a standalone MCP server project | `--app-name`, `--base-url` | | `glirastes generate ` | Generate endpoint tools from an OpenAPI spec | `--out ` | | `glirastes check-upgrade` | Analyze release notes for breaking changes against your code | `--from-version ` | | `glirastes sync` | Upload tool schemas to the Glirastes platform (Pro) | `--api-key` (or `GLIRASTES_API_KEY` env) | For the full deep-dive, see [maintaining-glirastes-tools](../maintaining-glirastes-tools/SKILL.md). ## Common pitfalls | Symptom | Cause | Fix | |---|---|---| | `Cannot resolve '@ai-sdk/react'` at build | Optional peer not installed | `npm install @ai-sdk/react react-markdown` | | `Cannot resolve 'wavesurfer.js/dist/plugins/record.esm.js'` | Voice input components reachable in bundle without peer | Either install `wavesurfer.js` or set `showMic={false}` on `` | | Chat widget unstyled | `styles.css` not imported | Add `import 'glirastes/react/styles.css'` to root layout | | Tool never fires | `path` mismatches the actual route | Endpoint tool's `path` must match the URL the route handler serves exactly | | `OPENAI_API_KEY is not configured` | Env var missing | `.env.local` + restart dev server (Next caches env at startup) | | `endpointToolRegistry is undefined` | Codegen script never ran | `npm run predev` or run `glirastes generate-tools` once manually | | LLM ignores a tool | Description missing or vague | Rewrite to *"WHEN to call: ... WHAT it returns: ..."* in the description | | UI action `task-details.open` fires but nothing happens | No `useAiClientAction` registered | Add handler in a mounted component; verify with `glirastes coverage` | | OpenAPI spec generation fails | Spec not validated | Run `glirastes validate ` first | ## What this skill does NOT cover - **NestJS adapter** — see [integrating-glirastes-nestjs](../integrating-glirastes-nestjs/SKILL.md) - **Tool authoring deep-dive** (UI patterns, output schemas, approvals, `module`/`sharedWith`, classification metadata) — see [maintaining-glirastes-tools](../maintaining-glirastes-tools/SKILL.md) - **Chat UI customization** (theming, mentions, voice input, approval cards, action bus, ChatWindow portal) — see [building-glirastes-chat-ui](../building-glirastes-chat-ui/SKILL.md) - **LangGraph transport** — replace `createAiChatHandler` + `VercelAiChat` with the LangGraph variants from `glirastes/server/nextjs` and `glirastes/react/langgraph` - **Glirastes platform features** (PII Shield via `createPiiShield`, intent routing via `createLancer`, guardrails, usage telemetry) — opt in via `GLIRASTES_API_KEY` env var; the SDK falls back to free-tier behaviour without it