ChatGPT API Tutorial – Build a Chatbot with NextJS

Chatbots have become increasingly popular over the years, and for a good reason. They offer businesses and individuals an easy way to communicate with their audience and customers, 24/7. With the advancements in Natural Language Processing (NLP) technology, building a chatbot that can hold human-like conversations has become easier than ever. In this ChatGPT API tutorial, we will guide you through building your own chatbot using ChatGPT API, NextJS, TailwindCSS, and DaisyUI. Not only that, but we also have a video tutorial that accompanies this article, providing a step-by-step guide for those who prefer visual aids.

Let’s get started!

OpenAI API Tutorial

Table of Contents

Note: The finished project can be cloned from GitHub.

Create Project & Install Dependencies

First, we create a new project using TypeScript. Leave everything during the setup process on default. You can call your application what you like; we call it openai-chatgpt.

npx create-next-app@latest --typescript
cd openai-chatgpt
Code language: Bash (bash)

Then we need to install all of the dependencies:

npm install openai
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
npm install daisyui
Code language: Bash (bash)

Setup TailwindCSS and DaisyUI

Open tailwind.config.js and paste the code below:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",

    // Or if using `src` directory:
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [require("daisyui")],
}
Code language: JavaScript (javascript)

Open /styles/global.css and paste the code below, overwriting everything in it:

@tailwind base;
@tailwind components;
@tailwind utilities;

html,
body {
  padding: 0;
  margin: 0;
  width: 100%;
}

a {

  text-decoration: none;
}

* {
  box-sizing: border-box;
}
Code language: CSS (css)

You can delete all other files inside of the /styles/ folder so that only global.css remains.

Setting up OpenAI

First, you need to create a OpenAI account. They offer a free trial so you can start testing right away. Once your account is created, navigate to API Keys and create a new secret key.

Note: Never share this key or secret with anyone.

Once the key is created, make sure to copy it.

Environment Variables

Create a new file in your root folder called .env.local and add a variable called OPENAI_API_KEY to it. Paste your OpenAI API key in quotes next to it like so:

OPENAI_API_KEY="sk-ZbuaBakBei3mfngn3n12BlbkFJCHAxmG3c************"
Code language: JavaScript (javascript)

OpenAI API Endpoint

Create a file called /pages/api/openAIChat.ts and paste the following code:

import type { NextApiRequest, NextApiResponse } from "next"
import { Configuration, OpenAIApi } from "openai"

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
})
const openai = new OpenAIApi(configuration)

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  console.log("req.body", req.body.messages)
  const completion = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: req.body.messages,
  })

  res.status(200).json({ result: completion.data })
}
Code language: TypeScript (typescript)

This is an API endpoint function that can be used with Next.js to create a chatbot. It uses the OpenAI API to generate responses to user messages in a chat interface.

The import statements at the beginning of the code import necessary libraries for the code to work.

const configuration creates a new OpenAI API configuration object that includes an API key, which is stored in the process.env.OPENAI_API_KEY environment variable.

const openai creates a new instance of the OpenAIApi class, which is used to interact with the OpenAI API.

The export default async function handler is the actual API endpoint function that receives incoming requests and sends back responses. It takes in a NextApiRequest object, which contains the request data, and a NextApiResponse object, which is used to send the response back to the client.

Inside the function, console.log("req.body", req.body.messages) logs the messages received in the request body.

const completion is a variable that stores the result of the openai.createChatCompletion() function, which sends the user’s message to the OpenAI API and generates a response. The model parameter specifies the language model to use, in this case, “gpt-3.5-turbo”. The messages parameter contains an array of messages in the chat interface.

Finally, res.status(200).json({ result: completion.data }) sends a JSON response to the client with the generated response from the OpenAI API. The completion.data property contains the actual response text.

In summary, this API endpoint function uses the OpenAI API to generate responses to user messages in a chat interface and then sends those responses back to the client.

Chatbot Logic

Next, we are writing our chatbot logic. Open the index.tsx file and delete everything in it. Paste the code below, so we start with a clean slate:

import React, { useRef } from "react"

export default function Home() {

return (
<div className='w-full'>
  <div className='flex flex-col items-center justify-center mt-40 text-center'>
    <h1 className='text-6xl'>Hi there, I am AVA</h1>
	</div>
</div>
  )
}
Code language: TypeScript (typescript)

This is a React component that returns a simple HTML layout with a title, using the TailwindCSS library for styling.

The import statement imports React and the useRef hook from the react library.

The export default function Home() function defines the React component.

The function returns a div with two nested divs. The outermost div has a w-full class, which sets the width of the div to 100% of the parent container.

The innermost div has flex, flex-col, items-center, justify-center, mt-40, and text-center classes. These classes use the TailwindCSS utility classes to create a flexbox container that is vertically and horizontally centered on the page, with 40px of margin from the top.

The h1 element inside the innermost div has a text-6xl class, which sets the font size to 6 times the default font size. The text inside the h1 element is “Hi there, I am AVA”, which is the title of the chatbot.

In summary, this code creates a simple React component that displays a title for the chatbot using TailwindCSS for styling.

States

Next, we need to create some states that we will use for our Chatbot:

...
export default function Home() {
  // States
  const [value, setValue] = React.useState<string>("")
  const [conversation, setConversation] = React.useState<Conversation[]>([])
  const inputRef = useRef<HTMLInputElement>(null)
...
Code language: TypeScript (typescript)

Input Handler

Next, we need to add a way to enter a prompt and handle the input:

import React, { useRef } from "react"

export default function Home() {
  // States
  const [value, setValue] = React.useState<string>("")
  const [conversation, setConversation] = React.useState<Conversation[]>([])
  const inputRef = useRef<HTMLInputElement>(null)

  const handleInput = React.useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setValue(e.target.value)
    },
    []
  )

return (
    <div className='w-full'>
      <div className='flex flex-col items-center justify-center mt-40 text-center'>
        <h1 className='text-6xl'>Hi there, I am AVA</h1>
        <div className='my-12'>
          <p className='mb-6 font-bold'>Please type your prompt</p>
          <input
            placeholder='Type here'
            className='w-full max-w-xs input input-bordered input-secondary'
            value={value}
            onChange={handleInput}
            onKeyDown={handleKeyDown}
          />
        </div>
)
}
Code language: TypeScript (typescript)
  • value: This state is defined using the useState hook to keep track of the current value of the input field. The initial value is an empty string. The setValue function is used to update the value state when the input field changes.
  • conversation: This state is also defined using the useState hook to keep track of the conversation history. It is an array of conversation objects, where each object contains a user message and the chatbot’s response. The setConversation function is used to update the conversation state.
  • inputRef: This state is defined using the useRef hook to create a reference to the input field. This is useful for focusing the input field programmatically later on in the code.
  • handleInput: This function is defined using the useCallback hook to handle changes to the input field. It takes in an event parameter that represents the change event, and updates the value state with the new value of the input field. This function is passed as the onChange handler for the input field.
  • The input field: This field is defined with several properties, including placeholder, className, value, onChange, and onKeyDown. The placeholder property sets the text to display when the input field is empty. The className property sets the styling for the input field using TailwindCSS classes. The value property is set to the current value of the input field stored in the value state. The onChange property is set to the handleInput function, which is called when the input field changes. The onKeyDown property is not defined yet, but it will be added later to handle submitting the user’s message to the chatbot.

In summary, the newly added code defines states for value and conversation, creates a reference to the input field using inputRef, defines a handleInput function to handle changes to the input field, and adds an input field with several properties to capture the user’s prompt.

Chatbot Logic

Now we need to implement the chatbot logic:

import React, { useRef } from "react"

interface Conversation {
  role: string
  content: string
}

export default function Home() {
  // States
  const [value, setValue] = React.useState<string>("")
  const [conversation, setConversation] = React.useState<Conversation[]>([])
  const inputRef = useRef<HTMLInputElement>(null)

  const handleInput = React.useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setValue(e.target.value)
    },
    []
  )

  const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      const chatHistory = [...conversation, { role: "user", content: value }]
      const response = await fetch("/api/openAIChat", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ messages: chatHistory }),
      })

      const data = await response.json()
      setValue("")
      setConversation([
        ...chatHistory,
        { role: "assistant", content: data.result.choices[0].message.content },
      ])
    }
  }
  return (
    <div className='w-full'>
      <div className='flex flex-col items-center justify-center mt-40 text-center'>
        <h1 className='text-6xl'>Hi there, I am AVA</h1>
        <div className='my-12'>
          <p className='mb-6 font-bold'>Please type your prompt</p>
          <input
            placeholder='Type here'
            className='w-full max-w-xs input input-bordered input-secondary'
            value={value}
            onChange={handleInput}
            onKeyDown={handleKeyDown}
          />
        </div>
        <div className='textarea'>
          {conversation.map((item, index) => (
            <React.Fragment key={index}>
              <br />
              {item.role === "assistant" ? (
                <div className='chat chat-end'>
                  <div className='chat-bubble chat-bubble-secondary'>
                    <strong className='badge badge-primary'>AVA</strong>
                    <br />
                    {item.content}
                  </div>
                </div>
              ) : (
                <div className='chat chat-start'>
                  <div className='chat-bubble chat-bubble-primary'>
                    <strong className='badge badge-secondary'>User</strong>
                    <br />
                    {item.content}
                  </div>
                </div>
              )}
            </React.Fragment>
          ))}
        </div>
      </div>
    </div>
  )
}
Code language: TypeScript (typescript)

This newly added code is responsible for submitting the user’s prompt to the chatbot API, receiving a response, and updating the conversation history with the new message. Here’s an explanation of the new code:

  • interface Conversation: This interface defines the shape of each conversation item in the conversation state. It includes two properties: role, which is a string that indicates whether the message is from the user or the chatbot, and content, which is the text content of the message.
  • handleKeyDown: This function is called when the user presses a key while focused on the input field. It takes in an event parameter that represents the keyboard event. If the key pressed is “Enter”, the function sends a POST request to the /api/openAIChat endpoint with the current chat history and the user’s message. The function then parses the response JSON data, resets the input field’s value, and updates the conversation state with the chat history and the chatbot’s response.
  • The textarea div: This div contains the chat history, which is an array of conversation objects. The conversation.map function is used to iterate over each item in the conversation and render it as a chat bubble. Each bubble is either from the user or the chatbot, depending on the role property of the conversation object. The chat bubbles are styled using TailwindCSS classes.

In summary, this code handles submitting the user’s prompt to the chatbot API, receiving a response, and updating the conversation history with the new message. The conversation history is displayed in the chat interface as a series of chat bubbles styled using TailwindCSS.

Reset Conversations

Finally, we want to be able to reset our dialogue and implement a button, therefore:

import React, { useRef } from "react"

interface Conversation {
  role: string
  content: string
}

export default function Home() {
  // States
  const [value, setValue] = React.useState<string>("")
  const [conversation, setConversation] = React.useState<Conversation[]>([])
  const inputRef = useRef<HTMLInputElement>(null)

  const handleInput = React.useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setValue(e.target.value)
    },
    []
  )

  const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "Enter") {
      const chatHistory = [...conversation, { role: "user", content: value }]
      const response = await fetch("/api/openAIChat", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ messages: chatHistory }),
      })

      const data = await response.json()
      setValue("")
      setConversation([
        ...chatHistory,
        { role: "assistant", content: data.result.choices[0].message.content },
      ])
    }
  }

  const handleRefresh = () => {
    inputRef.current?.focus()
    setValue("")
    setConversation([])
  }

  return (
    <div className='w-full'>
      <div className='flex flex-col items-center justify-center mt-40 text-center'>
        <h1 className='text-6xl'>Hi there, I am AVA</h1>
        <div className='my-12'>
          <p className='mb-6 font-bold'>Please type your prompt</p>
          <input
            placeholder='Type here'
            className='w-full max-w-xs input input-bordered input-secondary'
            value={value}
            onChange={handleInput}
            onKeyDown={handleKeyDown}
          />
          <button
            className='btn btn-primary btn-xs mt-6'
            onClick={handleRefresh}
          >
            Start New Conversation
          </button>
        </div>
        <div className='textarea'>
          {conversation.map((item, index) => (
            <React.Fragment key={index}>
              <br />
              {item.role === "assistant" ? (
                <div className='chat chat-end'>
                  <div className='chat-bubble chat-bubble-secondary'>
                    <strong className='badge badge-primary'>AVA</strong>
                    <br />
                    {item.content}
                  </div>
                </div>
              ) : (
                <div className='chat chat-start'>
                  <div className='chat-bubble chat-bubble-primary'>
                    <strong className='badge badge-secondary'>User</strong>
                    <br />
                    {item.content}
                  </div>
                </div>
              )}
            </React.Fragment>
          ))}
        </div>
      </div>
    </div>
  )
}
Code language: TypeScript (typescript)

This final addition adds a “Start New Conversation” button to the chat interface. When the button is clicked, the chat history and input field are reset to their initial values. Here’s an explanation of the new code:

  • handleRefresh: This function is called when the “Start New Conversation” button is clicked. It uses the inputRef reference to focus on the input field, sets the value state to an empty string, and sets the conversation state to an empty array.
  • The button: This button is added to the bottom of the input field using TailwindCSS classes. It has a “btn-primary” style and calls the handleRefresh function when clicked.

In summary, this final addition adds a “Start New Conversation” button to the chat interface, allowing the user to reset the chat history and input field to their initial values. The button is styled using TailwindCSS classes and calls the handleRefresh function when clicked.

Conclusion

The article is a tutorial on how to build a chatbot using ChatGPT API, NextJS, TailwindCSS, and DaisyUI. It walks through the steps of setting up the NextJS app, configuring the ChatGPT API endpoint, creating a conversation history, styling the chat interface with TailwindCSS and DaisyUI, handling user input and sending it to the ChatGPT API, and displaying the chatbot’s response.

The tutorial also shows how to add a “Start New Conversation” button to the chat interface to allow the user to reset the conversation history and input field. Overall, the tutorial provides a comprehensive guide on how to build a functional chatbot using modern web development tools.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Share via
Copy link
Powered by Social Snap