Build a RAG Chatbot with Upstash, OpenAI, Clerk, and Next.js
In this post, you will learn how to build your own career coach with Upstash Vector, Upstash Redis, OpenAI API, Clerk, Next.js App Router, and Vercel. Upstash RAG Chat SDK simplifies the per-user management of vector embeddings, chat history, and dynamic (with latest) relevant context generation. Specifically, Upstash Redis stores the per-user chat history, while Upstash Vector handles all vector embeddings within each user's namespace.
Prerequisites
You will need the following:
- Node.js 18 or later
- A Clerk account
- An Upstash account
- An OpenAI account
- A Vercel account
Tech Stack
- Next.js :The React Framework for the Web.
- Clerk :User Management Platform. You are going to use it to add authentication to your application.
- Upstash :Serverless database platform. You are going to use both Upstash Vector and Upstash Redis for storing vector embeddings and conversation history.
- OpenAI :An artificial intelligence research lab focused on developing advanced AI technologies.
- Vercel :A cloud platform for deploying and scaling web applications.
Generate an OpenAI Token
Using OpenAI API, you are able to create chatbot responses using AI. Any request to OpenAI API requires an authorization token. To obtain the token, navigate to the API Keys in your OpenAI account, and click the Create new secret key button. Copy and securely store this token for later use as OPENAI_API_KEY environment variable.
Setting up Upstash Redis
In your Upstash dashboard, go to Redis tab and create a database.
Scroll down until you find the REST API section, and select the .env
button. Copy the content and save it somewhere safe.
Setting up Upstash Vector
In your Upstash dashboard, go to Vector tab and create an Index.
Also, scroll down until you find the Connect section, and select the .env button. Copy the content and save it somewhere safe.
Create a new Clerk application
In your Clerk Dashboard, to create a new app, press the + New application card to interactively start curating your own authentication setup form.
With an application name of your choice, enable user authentication via credentials by toggling on Email and allow user authentication via Social Sign-On by toggling on providers such as Google, GitHub and Microsoft.
Once the application is created in the Clerk dashboard, you will be shown with your application's API keys for Next.js. Copy the content and save it somewhere safe.
Create a new Next.js application
Let’s get started by creating a new Next.js project. Open your terminal and run the following command:
When prompted, choose:
Yes
when prompted to use TypeScript.No
when prompted to use ESLint.Yes
when prompted to use Tailwind CSS.No
when prompted to usesrc/
directory.Yes
when prompted to use App Router.No
when prompted to customize the default import alias (@/*
).
Once that is done, move into the project directory and start the app in developement mode by executing the following command:
The app should be running on localhost:3000. Stop the development server to install the necessary dependencies with the following command:
The libraries installed include:
ai
: A library to build AI-powered streaming text and chat UIs.@clerk/nextjs
: Clerk’s SDK for Next.js.@upstash/vector
: A connectionless (HTTP based) Vector client.@upstash/rag-chat
: Batteries included SDK for developing RAG applications.
Now, create a .env
file at the root of your project. You are going to add the OPENAI_API_KEY
, CLERK_SECRET_KEY
, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
, UPSTASH_REDIS_REST_URL
, UPSTASH_REDIS_REST_TOKEN
, UPSTASH_VECTOR_REST_URL
and UPSTASH_VECTOR_REST_TOKEN
values you obtained earlier. It should look something like this:
To create API endpoints in Next.js, you will use Next.js Route Handlers which allow you to serve responses over Web Request and Response APIs. To start creating API routes in Next.js that streams responses to the user, execute the following commands:
The -p
flag creates parent directories of a directory if they're missing.
This sets up our Next.js project. Now, let's set up Clerk in the application.
Set up Clerk SDK with Next.js
Clerk has a Next.js SDK that contains helpers to make implementation of sign in modal, and managing (authenticated) sessions easier. You will add the ClerkProvider
component to the global layout of your Next.js application. This is a critical component as it provides access to the active session, and user context to all of Clerk’s components present anywhere in the application.
Make the following additions in app/layout.tsx
to wrap the whole Next.js application with ClerkProvider
component:
Now, let's move on to configuring the Next.js middleware for managing sessions with Clerk.
Configure Next.js Middleware for Clerk
Clerk requires middleware to allow granular control of protection via authentication over routes (including router handlers) on a per-request basis. Create a middleware.ts
file at the root of your project with the following:
The code above imports Clerk's clerkMiddleware
helper extending the ability to mark specific routes as public (i.e. they are accessible without authentication), as ignored (i.e. authentication checks are not ran on such pages), and as API Routes (i.e. they are treated by Clerk as API Endpoints). The middleware is applied to the route paths matching the matcher
option in config
object, which per the above config is all non-static assets paths.
Now, let's integrate shadcn/ui
components in Next.js.
Integrating shadcn/ui components
To quickly prototype the user interface, you will set up the shadcn/ui
with Next.js. shadcn/ui
is a collection of beautifully designed components that you can copy and paste into your apps. To set up shadcn/ui
, execute the command below:
You will be asked a few questions to configure a components.json
, choose the following:
Yes
when prompted to use TypeScript.Slate
when prompted to choose the base color.yes
when prompted to use CSS variables for colors.Yes
when prompted to proceed with writing the configuration to components.json.
Once that is done, you have set up a CLI that allows us to easily add React components to your Next.js application. Next, execute the command below to get the button, input, tooltip, and toast elements:
Once that is done, you would now see a ui
directory inside the app/components
directory containing button.tsx
, input.tsx
, tooltip.tsx
, toast.tsx
, toaster.tsx
, and use-toast.ts
.
Next, open up the app/layout.tsx
file, and make the following additions:
In the code changes above, you have imported the Toaster
component, and made sure that it is present in your entire Next.js application. It enables you to show toast notifications from anywhere in your code via the useToast
React hook.
Now, let's define a function to retrieve the signed in user details.
Fetch User Object with Clerk
In the getUserSession
function below, it fetches the authenticated user's object using Clerk's currentUser
utility. If the signed in user has a valid ID, it returns an array containing session and document identifiers based on the user's ID.
Now, let's move on to define an instance of Upstash's RAG Chat SDK.
Instantiating Upstash RAG Chat SDK with OpenAI
Upstash's RAG Chat SDK aims at simplifying the developer experience for creating rag applications, via a simplified method of instantiating an instance, generating vector embeddings of any kind of data, and querying the AI with the dynamically generated context-aware for a response.
To define an instance, create a file named rag.server.ts
in the lib
directory with the following code:
In the code above, a new RAG Chat instance using the OpenAI model gpt-3.5-turbo
with a prompt template for the chat interaction is defined, aiming to provide career advice based on context and chat history.
With the utilities setup, let's move on to creating API endpoints for ingesting PDFs and streaming chat responses from AI.
Chat History API Endpoint in Next.js App Router
Create a file named route.ts
in the app/api/chat/history
directory that retrieves the chat history of a user with the following code:
The code above retrieves upto 100 chat messages for the authenticated user and returns them as a JSON response. If the user session is not found, it responds with a 403 status.
Now, let's move on to create a chat endpoint using the RAG Chat SDK.
Chat API Endpoint in Next.js App Router
To create an endpoint that handles user chat interactions with the latest context, create a file named route.ts
in the app/api/chat
directory with the following code:
The code above fetches the user session and chat messages, verifies the user's session, and adds relevant context dynamically. Then, it returns a streaming response using the aiUseChatAdapter
function.
Now, let's move on to create a context endpoint using the RAG Chat SDK.
Context API Endpoint in Next.js App Router
To create an endpoint that allows authenticated users to upload a PDF file and add its vector embeddings' to the vector index, you will create a file named route.ts
in the app/api/upsert
directory with the following code:
The code above defines a POST method that fetches the user session and uploaded file, validates them, converts the file to a Blob, and adds it to the vector index under the user's namespace.
With a per-user namespace, you easily seperate the chat context for each user.
Building the chatbot interface
To start building the application's user interface, open the app/page.tsx
file and replace the existing code with the following:
The code above begins with importing the React hooks for managing the state of the user, the conversation, and invoke toast elements. As the component is mounted (and if the user's logged in state changes), the chat history for the user is fetched.
Now, perform the following additions to be able to add user-uploaded PDF(s) to their vector index namespace:
The code above defines an input
element that only accepts a pdf file from the user. When a PDF is uploaded, via the onChange
callback, it POST
s the file to /api/upsert
api endpoint. Once that's done, it displays a toast indiciating if the operation was a success.
Now, perform the following additions to switch states depending on user authentication status:
The code above creates a signed in and out state of the application for the user. If the user is signed in, it displays their image via UserButton
component with additional actions, and a default loading state of their chat history. If the user is signed out, it shows a Sign in to use Nerdcoach
button.
Now, perform the following additions to add an input for user to submit their prompt and upload PDF file:
The code above defines an input
element which after the user submits their prompt, calls the /api/chat
endpoint. It also defines a TooltipProvider
component which invokes the PDF file uploader input
if a user clicks the upload icon.
That was a lot of learning! You’re all done now ✨
Deploy to Vercel
The repository, is now ready to deploy to Vercel. Use the following steps to deploy 👇🏻
- Start by creating a GitHub repository containing your app's code.
- Then, navigate to the Vercel Dashboard and create a New Project.
- Link the new project to the GitHub repository you just created.
- In Settings, update the
Environment Variables
to match those in your local.env
file. - Deploy! 🚀
More Information
For more detailed insights, explore the references cited in this post.
Conclusion
In this blog, you learned how to build a career coach application using Upstash Vector, Upstash Redis, OpenAI API, Clerk, Next.js App Router, and Vercel. You set up Upstash to manage vector embeddings and chat history, configured Clerk for user authentication, and integrated OpenAI for AI-powered responses. The blog walked you through creating API endpoints for chat interactions, adding context from user-uploaded PDFs, and building a user interface with authentication and file upload functionalities.