Skip to content

Basic Next.js AI Chat

Want to view the full code? Check out the basic-nextjs-ai-chat repository on GitHub.

Create a new Next.js project

Next.js recommends starting a new Next.js app using create-next-app, which sets up everything automatically for you. To create a project, run:

shell
npx create-next-app@latest

Accept all the defaults.

Environment Variables

Create .env.local file in the root of your project and add the following variables:

text
OPENAI_API_KEY=xxxxxx

INVSY_API_KEY=xxxxxx
INVSY_PROJECT_ID=xxxxxx

Get your Invsy secret key and project ID from the Invsy dashboard

Install packages & create the Invsy instance

Install the invsy, ai and @ai-sdk/openai SDKs by running:

shell
npm add invsy ai @ai-sdk/openai

Then create the Invsy instance in a new file ./src/libs/invsy.ts:

ts
import { Invsy } from "invsy"

// Add your own auth logic
export const userId = "user1"

export const invsy = new Invsy({
    token: process.env.INVSY_API_KEY!,
    projectId: process.env.INVSY_PROJECT_ID!,
    userId
})

Update the root page

Replace the contents of ./src/page.tsx with the following code:

tsx
import { invsy } from "@/libs/invsy";
import { redirect } from "next/navigation";

export default async function Page() {
  // Create new chat and returns the chat id
  const { id } = await invsy.create({
    title: "new chat"
  })

  redirect(`/${id}`)
}

Create the ChatUI component

tsx
"use client"
import { useChat } from "ai/react";
import { InvsyChat } from "invsy";

type Props = {
    chat: InvsyChat
}

export const ChatUi = ({ chat }: Props) => {
    const { messages, input, handleInputChange, handleSubmit } = useChat({
        api: `/api/chat/${chat.id}`,
        keepLastMessageOnError: true,
        initialMessages: chat.messages ? chat.messages : [],
    });

    return (
        <>
            {messages.map((message, index) => (
                <div key={index}>
                    {message.role === 'user' ? 'User: ' : 'AI: '}
                    {message.content}
                </div>
            ))}

            <form onSubmit={handleSubmit}>
                <input name="prompt" value={input} onChange={handleInputChange} />
                <button type="submit">Submit</button>
            </form>
        </>
    );
}

Create the chat page

Create a new file ./src/app/[chat_id]/page.tsx and add the following code:

tsx
import { ChatUi } from "@/components/chat-ui";
import { invsy } from "@/libs/invsy";

type Props = {
    params: {
        chat_id: string;
    };
}

export default async function ChatPage({params}: Props) {
    const chatId = params.chat_id
    const chat = await invsy.get(chatId);

    return (
        <>
            <ChatUi chat={chat}/>
        </>
    );
}

Create the chat API

Create a new file ./src/app/api/chat/[chat_id]/route.ts and add the following code:

ts
import {createOpenAI, openai} from '@ai-sdk/openai';
import { convertToCoreMessages, streamText } from 'ai';
import { invsy } from "@/libs/invsy";
import { InvsyChatPartial } from "invsy";

// Allow streaming responses up to 30 seconds
export const maxDuration = 30;

export async function POST(req: Request, { params }: { params: { chat_id: string } }) {
    const { messages } = await req.json();

    const openai = createOpenAI({
        apiKey: process.env.OPENAI_API_KEY,
        baseURL: process.env.OPENAI_API_BASE
    })

    const result = await streamText({
        model: openai('gpt-4-turbo'),
        system: 'You are a helpful assistant.',
        messages: convertToCoreMessages(messages),
        // You can determine message role by checking toolCalls, toolResults etc.
        async onFinish({ text, toolCalls, toolResults }) {
            messages.push({
                role: 'assistant',
                content: text
            })

            const payload: InvsyChatPartial = {
                id: params.chat_id,
                messages
            }

            // Update the chat title
            if (messages.length <= 2) {
                payload.meta = {
                    // Get the content of the first message and limit to 100 chars
                    title: messages[0].content.slice(0, 100),
                }
            }

            await invsy.save(payload);

        },
    });

    return result.toDataStreamResponse();
}

Run npm run dev and navigate to http://localhost:3000 in your browser. Add a prompt in the input field and click submit. You should see the AI response in the chat window.

Refresh the page and you should see the chat history!

Congradulations! You have successfully created a basic AI chat app using Next.js and Invsy. You can now customize the chat UI and add more features to your app.