So, the next step on our journey now is to actually persist the messages that we get from the LLM and our own user messages to a database of some sort. To spare you any trouble of setting up a Postgres database, I've provided you with a persistence layer.
This persistence layer has a few functions that you'll need to know about:
// Available functionsloadChats();saveChats();createChat();getChat();appendToChatMessages();deleteChat();
The main two you'll need to know about are:
getChat: Retrieves chats and their associated messagesappendToChatMessages: Takes a chatId and messages and appends them to the chat historygetChat and appendToChatMessagesLet's take a look at the frontend first. I've added a backupChatId piece of state:
// This provides a stable chatId for when we're creating a new chatconst [backupChatId, setBackupChatId] = useState(crypto.randomUUID(),);const [searchParams, setSearchParams] = useSearchParams();const chatIdFromSearchParams = searchParams.get('chatId');
The idea is that when we're at localhost:3000 without any chatId in the URL, we want a valid stable chatId to be passed - one that doesn't change every render.
I've also added React Query code to fetch the chat from the backend:
const { data } = useSuspenseQuery({queryKey: ['chat', chatIdFromSearchParams],queryFn: () => {if (!chatIdFromSearchParams) {return null;}return fetch(`/api/chat?chatId=${chatIdFromSearchParams}`,
This takes the chatId from search params and fetches from the /api/chat endpoint.
We need to pass the chatId to the useChat hook as well as any existing messages:
// TODO: pass the chatId to the useChat hook,// as well as any existing messages from the backendconst { messages, sendMessage } = useChat({});
useChat hook to include the chatIduseChat hookLooking at the backend code for /api/chat, the GET endpoint is already implemented. It's just fetching the chat from the database:
export const GET = async (req: Request): Promise<Response> => {const url = new URL(req.url);const chatId = url.searchParams.get('chatId');if (!chatId) {return new Response('No chatId provided', { status:
The POST endpoint is where we need to do most of our work:
export const POST = async (req: Request): Promise<Response> => {const body: { messages: UIMessage[]; id: string } =await req.json();
getChat(id)createChat(id, messages)result.toUIMessageStreamResponse() to save AI response messageFinally, we need to update the form submission handler:
onSubmit={(e) => {e.preventDefault();sendMessage({text: input,});setInput('');// TODO: set the search params to the new chatId// if the chatId is not already set// TODO: refresh the backup chatId// if the chatId is not already set}}