|
| 1 | +--- |
| 2 | +title: "Build a Next.js App with BaseAI" |
| 3 | +description: "Learn how to build a Next.js app with BaseAI" |
| 4 | +tags: |
| 5 | + - nextjs |
| 6 | + - baseai |
| 7 | + - langbase |
| 8 | + - guide |
| 9 | +section: 'guides' |
| 10 | +published: 2024-09-30 |
| 11 | +modified: 2024-09-30 |
| 12 | +--- |
| 13 | + |
| 14 | +# Build a Next.js App with BaseAI |
| 15 | + |
| 16 | +This guide covers building AI features in a Next.js app using BaseAI, focusing on text summarization with the BaseAI pipe. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## Next.js with BaseAI Example |
| 21 | + |
| 22 | +We also have pre-built a Next.js app with BaseAI for you to get started quickly. You can refer to the following resources to understand how to build a Next.js app with BaseAI. |
| 23 | + |
| 24 | +- [Next.js with BaseAI](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs) |
| 25 | +- [Example of using BaseAI](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs/app/demo) |
| 26 | +- [BaseAI based React components](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs/components) |
| 27 | +- [API Route handlers for pipes](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs/app/api/langbase/pipes) |
| 28 | + |
| 29 | + |
| 30 | +### AI Agent Pipe: Run |
| 31 | + |
| 32 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/pipe-run.tsx) |
| 33 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run/route.ts) |
| 34 | + |
| 35 | +### AI Agent Pipe: Stream |
| 36 | + |
| 37 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/pipe-stream.tsx) |
| 38 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-stream/route.ts) |
| 39 | + |
| 40 | +### `usePipe()`: Chat |
| 41 | + |
| 42 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/chat-simple.tsx) |
| 43 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-stream/route.ts) |
| 44 | + |
| 45 | +### `usePipe()`: Chat Advanced |
| 46 | + |
| 47 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/chat-advanced.tsx) |
| 48 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-stream/route.ts) |
| 49 | + |
| 50 | +### AI Agent Pipes: Tool Calling |
| 51 | + |
| 52 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/pipe-run-with-tool.tsx) |
| 53 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-tool/route.ts) |
| 54 | + |
| 55 | +### AI Agent Pipes: Composable Pipe Run |
| 56 | + |
| 57 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/pipe-run-pipes-as-tools.tsx) |
| 58 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-pipes-as-tools/route.ts) |
| 59 | + |
| 60 | +### AI Agent Pipes: Memory |
| 61 | + |
| 62 | +- [React Component](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/components/pipe-run-with-memory.tsx) |
| 63 | +- [API Route Handler](https://github.com/LangbaseInc/baseai/blob/main/examples/nextjs/app/api/langbase/pipes/run-memory/route.ts) |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Step #1: Install Next.js |
| 68 | + |
| 69 | +First, you need to install Next.js in your project directory. |
| 70 | + |
| 71 | +```bash |
| 72 | +npx create-next-app@latest nextjs-baseai-app |
| 73 | +``` |
| 74 | + |
| 75 | +Also setup tailwind in your Next.js app. You can use the setup from the [Next.js with BaseAI example](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs). |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +## Step #2: Install BaseAI |
| 80 | + |
| 81 | +Next, you need to install BaseAI in your project directory. |
| 82 | + |
| 83 | +```bash |
| 84 | +npx baseai@latest init |
| 85 | +``` |
| 86 | +--- |
| 87 | + |
| 88 | +## Step #3: Create a Summary Pipe |
| 89 | + |
| 90 | +Create a new pipe using the `pipe` command. Use `summary` as the pipe name and for system prompt use `You are a helpful AI assistant. Make everything Less wordy.`. |
| 91 | + |
| 92 | +```bash |
| 93 | +npx baseai@latest pipe |
| 94 | +``` |
| 95 | + |
| 96 | +It creates a pipe at `baseai/pipes/summary.ts` in your current directory. |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## Step #4: Set Environment Variables |
| 101 | + |
| 102 | +Use following command to create a `.env` file in your project directory. |
| 103 | + |
| 104 | +```bash |
| 105 | +cp .env.baseai.example .env |
| 106 | +``` |
| 107 | + |
| 108 | +Set the `OPENAI_API_KEY` in the `.env` file. |
| 109 | + |
| 110 | +## Step #5: Add API Route Handler |
| 111 | + |
| 112 | +Create a new API route handler `app/api/langbase/pipes/run/route.ts` to use the pipe. |
| 113 | + |
| 114 | +<CodeGroup exampleTitle="API Route Handler" title="API Route Handler for Pipe Run"> |
| 115 | +```ts {{ title: 'app/api/langbase/pipes/run/route.ts' }} |
| 116 | +import {Pipe} from '@baseai/core'; |
| 117 | +import {NextRequest} from 'next/server'; |
| 118 | +import pipeSummary from '../../../../../baseai/pipes/summary'; |
| 119 | + |
| 120 | +export async function POST(req: NextRequest) { |
| 121 | + const runOptions = await req.json(); |
| 122 | + |
| 123 | + // 1. Initiate the Pipe. |
| 124 | + const pipe = new Pipe(pipeSummary()); |
| 125 | + |
| 126 | + // 2. Run the pipe |
| 127 | + const result = await pipe.run(runOptions); |
| 128 | + |
| 129 | + // 3. Return the response stringified. |
| 130 | + return new Response(JSON.stringify(result)); |
| 131 | +} |
| 132 | + |
| 133 | +``` |
| 134 | +</CodeGroup> |
| 135 | + |
| 136 | + |
| 137 | +## Step #6: Add React Component |
| 138 | + |
| 139 | +Add following to your Next.js app to run the pipe. |
| 140 | + |
| 141 | +- Pipe run **page** at `app/pipe-run/page.tsx` |
| 142 | +- Pipe run **component** at `components/pipe-run.tsx` — This component will run the pipe. |
| 143 | +- UI **Button** component at `components/ui/button.tsx` |
| 144 | +- UI **Input** component at `components/ui/input.tsx` |
| 145 | + |
| 146 | +Install the required dependencies. |
| 147 | + |
| 148 | +```bash |
| 149 | +npm install @radix-ui/react-slot class-variance-authority clsx tailwind-merge |
| 150 | +``` |
| 151 | + |
| 152 | +<CodeGroup exampleTitle="React Component" title="React Component for Pipe Run"> |
| 153 | +```tsx {{ title: 'app/pipe-run/page.tsx' }} |
| 154 | +import PipeRunExample from '@/components/pipe-run'; |
| 155 | + |
| 156 | +export default function Page() { |
| 157 | + return ( |
| 158 | + <div className="w-full max-w-md"> |
| 159 | + |
| 160 | + <h1 className="text-2xl font-light text-gray-800 mb-1 text-center"> |
| 161 | + ⌘ Langbase AI Agent Pipe: Run |
| 162 | + </h1> |
| 163 | + |
| 164 | + <p className="text-muted-foreground text-base font-light mb-20 text-center"> |
| 165 | + Run a pipe to generate a text completion |
| 166 | + </p> |
| 167 | + |
| 168 | + <PipeRunExample /> |
| 169 | + </div> |
| 170 | + ); |
| 171 | +} |
| 172 | +``` |
| 173 | + |
| 174 | +```tsx {{ title: 'components/pipe-run.tsx' }} |
| 175 | +'use client'; |
| 176 | + |
| 177 | +import {Button} from '@/components/ui/button'; |
| 178 | +import {Input} from '@/components/ui/input'; |
| 179 | +import {useState} from 'react'; |
| 180 | + |
| 181 | +export default function PipeRunExample() { |
| 182 | + const [prompt, setPrompt] = useState('Who are you?'); |
| 183 | + const [completion, setCompletion] = useState(''); |
| 184 | + const [loading, setLoading] = useState(false); |
| 185 | + |
| 186 | + const handleSubmit = async (e: any) => { |
| 187 | + e.preventDefault(); |
| 188 | + if (!prompt.trim()) return; |
| 189 | + |
| 190 | + setLoading(true); |
| 191 | + try { |
| 192 | + const response = await fetch('/api/langbase/pipes/run', { |
| 193 | + method: 'POST', |
| 194 | + headers: {'Content-Type': 'application/json'}, |
| 195 | + // Send prompt as an LLM message. |
| 196 | + body: JSON.stringify({ |
| 197 | + messages: [{role: 'user', content: prompt}], |
| 198 | + }), |
| 199 | + }); |
| 200 | + |
| 201 | + if (!response.ok) { |
| 202 | + throw new Error('Network response was not ok'); |
| 203 | + } |
| 204 | + |
| 205 | + // Parse the JSON response. |
| 206 | + const data = await response.json(); |
| 207 | + setCompletion(data.completion); |
| 208 | + } catch (error) { |
| 209 | + console.error('Error:', error); |
| 210 | + setCompletion('An error occurred while generating the completion.'); |
| 211 | + } finally { |
| 212 | + setLoading(false); |
| 213 | + } |
| 214 | + }; |
| 215 | + |
| 216 | + return ( |
| 217 | + <div className="bg-neutral-200 rounded-md p-2 flex flex-col gap-2 w-full"> |
| 218 | + <form |
| 219 | + onSubmit={handleSubmit} |
| 220 | + className="flex flex-col w-full items-center gap-2" |
| 221 | + > |
| 222 | + <Input |
| 223 | + type="text" |
| 224 | + placeholder="Enter prompt message here" |
| 225 | + value={prompt} |
| 226 | + onChange={e => setPrompt(e.target.value)} |
| 227 | + required |
| 228 | + /> |
| 229 | + |
| 230 | + <Button type="submit" className="w-full" disabled={loading}> |
| 231 | + {loading ? 'AI is thinking...' : 'Ask AI'} |
| 232 | + </Button> |
| 233 | + </form> |
| 234 | + |
| 235 | + {!loading && completion && ( |
| 236 | + <p className="mt-4"> |
| 237 | + <strong>AI:</strong> {completion} |
| 238 | + </p> |
| 239 | + )} |
| 240 | + </div> |
| 241 | + ); |
| 242 | +} |
| 243 | +``` |
| 244 | + |
| 245 | +```tsx {{ title: 'components/ui/button.tsx' }} |
| 246 | +import * as React from 'react'; |
| 247 | +import {Slot} from '@radix-ui/react-slot'; |
| 248 | +import {cva, type VariantProps} from 'class-variance-authority'; |
| 249 | + |
| 250 | +import {cn} from '@/lib/utils'; |
| 251 | + |
| 252 | +const buttonVariants = cva( |
| 253 | + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', |
| 254 | + { |
| 255 | + variants: { |
| 256 | + variant: { |
| 257 | + default: 'bg-primary text-primary-foreground hover:bg-primary/90', |
| 258 | + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', |
| 259 | + outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', |
| 260 | + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', |
| 261 | + ghost: 'hover:bg-accent hover:text-accent-foreground', |
| 262 | + link: 'text-primary underline-offset-4 hover:underline', |
| 263 | + }, |
| 264 | + size: { |
| 265 | + default: 'h-10 px-4 py-2', |
| 266 | + sm: 'h-9 rounded-md px-3', |
| 267 | + lg: 'h-11 rounded-md px-8', |
| 268 | + icon: 'h-10 w-10', |
| 269 | + }, |
| 270 | + }, |
| 271 | + defaultVariants: { |
| 272 | + variant: 'default', |
| 273 | + size: 'default', |
| 274 | + }, |
| 275 | + }, |
| 276 | +); |
| 277 | + |
| 278 | +export interface ButtonProps |
| 279 | + extends React.ButtonHTMLAttributes<HTMLButtonElement>, |
| 280 | + VariantProps<typeof buttonVariants> { |
| 281 | + asChild?: boolean; |
| 282 | +} |
| 283 | + |
| 284 | +const Button = React.forwardRef<HTMLButtonElement, ButtonProps>( |
| 285 | + ({className, variant, size, asChild = false, ...props}, ref) => { |
| 286 | + const Comp = asChild ? Slot : 'button'; |
| 287 | + return <Comp className={cn(buttonVariants({variant, size, className}))} ref={ref} {...props} />; |
| 288 | + }, |
| 289 | +); |
| 290 | +Button.displayName = 'Button'; |
| 291 | + |
| 292 | +export {Button, buttonVariants}; |
| 293 | +``` |
| 294 | + |
| 295 | +```tsx {{ title: 'components/ui/input.tsx' }} |
| 296 | +import * as React from 'react'; |
| 297 | + |
| 298 | +import {cn} from '@/lib/utils'; |
| 299 | + |
| 300 | +export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {} |
| 301 | + |
| 302 | +const Input = React.forwardRef<HTMLInputElement, InputProps>(({className, type, ...props}, ref) => { |
| 303 | + return ( |
| 304 | + <input |
| 305 | + type={type} |
| 306 | + className={cn( |
| 307 | + 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', |
| 308 | + className, |
| 309 | + )} |
| 310 | + ref={ref} |
| 311 | + {...props} |
| 312 | + /> |
| 313 | + ); |
| 314 | +}); |
| 315 | +Input.displayName = 'Input'; |
| 316 | + |
| 317 | +export {Input}; |
| 318 | +``` |
| 319 | + |
| 320 | +```tsx {{ title: 'lib/utils.ts' }} |
| 321 | +import {type ClassValue, clsx} from 'clsx'; |
| 322 | +import {twMerge} from 'tailwind-merge'; |
| 323 | + |
| 324 | +export function cn(...inputs: ClassValue[]) { |
| 325 | + return twMerge(clsx(inputs)); |
| 326 | +} |
| 327 | +``` |
| 328 | + |
| 329 | +</CodeGroup> |
| 330 | + |
| 331 | +Refer to [Next.js with BaseAI](https://github.com/LangbaseInc/baseai/tree/main/examples/nextjs) codebase for more details. |
| 332 | + |
| 333 | + |
| 334 | +--- |
| 335 | + |
| 336 | +## Step #7: Run the Next.js BaseAI App |
| 337 | + |
| 338 | +Run BaseAI dev server and start the Next.js app. |
| 339 | + |
| 340 | +```bash |
| 341 | +npx baseai@latest dev # Start BaseAI dev server |
| 342 | +npm run dev # Start Next.js app |
| 343 | +``` |
| 344 | + |
| 345 | +Open [http://localhost:3000/pipe-run](http://localhost:3000/pipe-run) to see the pipe run page. |
| 346 | + |
| 347 | +Write a prompt message and click on the `Ask AI` button to generate the completion. The AI response will be displayed below the button. This all happens locally on your machine. |
| 348 | + |
| 349 | +--- |
| 350 | + |
| 351 | +## Step #8: Deploy BaseAI project on Langbase |
| 352 | + |
| 353 | +To deploy the project on Langbase, you need to authenticate with your Langbase account. |
| 354 | + |
| 355 | +```bash |
| 356 | +npx baseai@latest auth |
| 357 | +``` |
| 358 | + |
| 359 | +After authentication, you can deploy the project using the following command. |
| 360 | + |
| 361 | +```bash |
| 362 | +npx baseai@latest deploy |
| 363 | +``` |
| 364 | + |
| 365 | +This will deploy your project on Langbase and you can access it as a highly scalable API. Check the [BaseAI `deploy` documentation](https://baseai.dev/docs/deployment/deploy) for more details. |
| 366 | + |
| 367 | +--- |
| 368 | + |
| 369 | +## Step #9: Deploy Next.js app |
| 370 | + |
| 371 | +Deploy your Next.js app using Vercel or any other hosting provider and set the following environment variables. |
| 372 | + |
| 373 | +```bash |
| 374 | +OPENAI_API_KEY="" #your_openai_api_key |
| 375 | +LANGBASE_API_KEY="" #your_langbase_api_key |
| 376 | +``` |
| 377 | + |
| 378 | +Langbase API key is the user or org API key that you authenticated with. You can obtain your [User/Org API Key](https://langbase.com/docs/api-reference/api-keys) from the Langbase dashboard. |
| 379 | + |
| 380 | +--- |
0 commit comments