Next.js
API route handlers, React Server Components, and client hooks for Next.js 13+ App Router.
Server-side integration. These handlers run the Gerbil class on the Node.js runtime. To run inference in the browser on the GPU instead, use the native WebGPU engine directly in a client component.
Installation
Terminal
npm install @tryhamster/gerbilAPI Route Handler
Create a simple API route that handles chat requests:
app/api/chat/route.ts
01// app/api/chat/route.ts02import { gerbil } from "@tryhamster/gerbil/next";03
04export const POST = gerbil.handler({05 model: "qwen3.5-0.8b",06});07
08// This creates an endpoint that accepts:09// POST /api/chat10// Body: { prompt: string, stream?: boolean, options?: {...} }11// Returns: { text: string, ... } or streams textHandler Options
app/api/ai/route.ts
01// app/api/ai/route.ts02import { gerbil } from "@tryhamster/gerbil/next";03
04export const POST = gerbil.handler({05 // Model configuration (GerbilConfig)06 model: "qwen3.5-0.8b",07 device: "auto", // "auto" | "webgpu"08 dtype: "q4", // "q4" | "q8" | "fp16" | "fp32"09
10 // Response caching11 cache: { enabled: true, ttl: 3600 },12
13 // Preload the model on startup so the first request is fast14 preload: true,15});16
17// Per-request generation options (maxTokens, temperature, system,18// thinking) come from the request body, not the handler config.Multiple Endpoints
Create multiple AI endpoints with a catch-all route:
app/api/ai/[...path]/route.ts
01// app/api/ai/[...path]/route.ts02import { gerbil } from "@tryhamster/gerbil/next";03
04const handlers = gerbil.createHandlers({05 model: "qwen3.5-0.8b",06});07
08export async function POST(09 req: Request,10 { params }: { params: { path: string[] } }11) {12 const path = params.path.join("/");13
14 switch (path) {15 case "generate":16 // Also streams: pass { stream: true } in the body17 return handlers.generate(req);18 case "json":19 return handlers.json(req);20 case "embed":21 return handlers.embed(req);22 default:23 return new Response("Not found", { status: 404 });24 }25}26
27export async function GET(28 req: Request,29 { params }: { params: { path: string[] } }30) {31 const path = params.path.join("/");32
33 switch (path) {34 case "info":35 return handlers.info(req);36 default:37 return new Response("Not found", { status: 404 });38 }39}40
41// Endpoints created:42// POST /api/ai/generate - Generate text (SSE stream with { stream: true })43// POST /api/ai/json - Structured JSON output44// POST /api/ai/embed - Generate embeddings45// GET /api/ai/info - Model infoStreaming Responses
Stream responses for real-time chat UIs:
app/api/stream/route.ts
01// app/api/stream/route.ts02import { gerbil } from "@tryhamster/gerbil/next";03
04// The handler streams Server-Sent Events when the request body05// includes { stream: true }:06export const POST = gerbil.handler({ model: "qwen3.5-0.8b" });07
08// POST /api/stream09// Body: { "prompt": "Tell me a story", "stream": true, "system": "..." }10// Response: data: {"chunk": "..."} events, ending with data: [DONE]Structured JSON Output
app/api/extract/route.ts
01// app/api/extract/route.ts02import { json } from "@tryhamster/gerbil";03import { z } from "zod";04
05const PersonSchema = z.object({06 name: z.string(),07 age: z.number(),08 email: z.string().email().optional(),09});10
11export async function POST(req: Request) {12 const { text } = await req.json();13
14 const data = await json(text, {15 model: "qwen3.5-0.8b",16 schema: PersonSchema,17 retries: 3,18 });19
20 return Response.json(data);21}React Server Components
Use Gerbil directly in Server Components:
app/summary/page.tsx
01// app/summary/page.tsx02import { generate } from "@tryhamster/gerbil";03
04// This runs on the server (the model loads on first use)05export default async function SummaryPage() {06 const summary = await generate(07 "Summarize: The quick brown fox jumps over the lazy dog.",08 { model: "qwen3.5-0.8b", maxTokens: 100 }09 );10
11 return (12 <div>13 <h1>Summary</h1>14 <p>{summary.text}</p>15 </div>16 );17}Client Components
Use the React hooks in client components:
app/chat/page.tsx
01// app/chat/page.tsx02"use client";03
04import { useGerbil } from "@tryhamster/gerbil/react";05import { useState } from "react";06
07export default function ChatPage() {08 const [input, setInput] = useState("");09 const [response, setResponse] = useState("");10 const { generate, stream, isLoading } = useGerbil({11 endpoint: "/api/ai",12 });13
14 const handleGenerate = async () => {15 const result = await generate(input);16 setResponse(result.text);17 };18
19 const handleStream = async () => {20 setResponse("");21 for await (const chunk of stream(input)) {22 setResponse((prev) => prev + chunk);23 }24 };25
26 return (27 <div className="p-4">28 <textarea29 value={input}30 onChange={(e) => setInput(e.target.value)}31 placeholder="Enter your prompt..."32 className="w-full p-2 border rounded"33 />34 <div className="flex gap-2 mt-2">35 <button onClick={handleGenerate} disabled={isLoading}>36 Generate37 </button>38 <button onClick={handleStream} disabled={isLoading}>39 Stream40 </button>41 </div>42 {response && (43 <div className="mt-4 p-4 bg-gray-100 rounded">44 {response}45 </div>46 )}47 </div>48 );49}Middleware
Add authentication or rate limiting:
app/api/ai/route.ts
01// app/api/ai/route.ts02import { generate } from "@tryhamster/gerbil";03import { getServerSession } from "next-auth";04
05export async function POST(req: Request) {06 // Check authentication07 const session = await getServerSession();08 if (!session) {09 return new Response("Unauthorized", { status: 401 });10 }11
12 // Rate limiting (example with upstash)13 const ip = req.headers.get("x-forwarded-for") || "anonymous";14 const { success } = await ratelimit.limit(ip);15 if (!success) {16 return new Response("Too many requests", { status: 429 });17 }18
19 // Process request20 const { prompt } = await req.json();21 const result = await generate(prompt, {22 model: "qwen3.5-0.8b",23 });24
25 return Response.json(result);26}Edge Runtime
Note: Gerbil requires Node.js runtime for model loading. For edge deployments, use a separate API server or serverless function.
app/api/ai/route.ts
// app/api/ai/route.ts// This route uses Node.js runtime (default)export const runtime = "nodejs";
import { gerbil } from "@tryhamster/gerbil/next";
export const POST = gerbil.handler({ model: "qwen3.5-0.8b",});Full Chat Application
Complete chat app with streaming, history, and thinking mode:
app/api/chat/[...path]/route.ts
01// app/api/chat/[...path]/route.ts02import { gerbil } from "@tryhamster/gerbil/next";03
04const handlers = gerbil.createHandlers({ model: "qwen3.5-0.8b" });05
06// useChat POSTs { prompt, ... } to /api/chat/generate (and07// /api/chat/stream with { stream: true }); one handler covers both.08export async function POST(req: Request) {09 return handlers.generate(req);10}app/chat/page.tsx
01// app/chat/page.tsx02"use client";03
04import { useState } from "react";05import { useChat } from "@tryhamster/gerbil/react";06
07export default function ChatApp() {08 const [thinking, setThinking] = useState(false);09 const {10 messages,11 input,12 setInput,13 handleSubmit,14 isLoading,15 } = useChat({16 endpoint: "/api/chat",17 });18
19 return (20 <div className="flex flex-col h-screen">21 {/* Messages */}22 <div className="flex-1 overflow-auto p-4 space-y-4">23 {messages.map((m, i) => (24 <div25 key={i}26 className={`p-3 rounded ${27 m.role === "user" ? "bg-blue-100 ml-auto" : "bg-gray-100"28 } max-w-[80%]`}29 >30 {m.content}31 </div>32 ))}33 </div>34
35 {/* Input */}36 <form onSubmit={handleSubmit} className="p-4 border-t">37 <div className="flex gap-2">38 <input39 value={input}40 onChange={(e) => setInput(e.target.value)}41 placeholder="Type a message..."42 className="flex-1 p-2 border rounded"43 disabled={isLoading}44 />45 <label className="flex items-center gap-1">46 <input47 type="checkbox"48 checked={thinking}49 onChange={(e) => setThinking(e.target.checked)}50 />51 Think52 </label>53 <button54 type="submit"55 disabled={isLoading}56 className="px-4 py-2 bg-blue-500 text-white rounded"57 >58 {isLoading ? "..." : "Send"}59 </button>60 </div>61 </form>62 </div>63 );64}