Can a serverless Postgres database really handle the demands of a real-time application? The answer lies in pairing it with the right publish-subscribe model. In this guide, you will learn how to combine the real-time capabilities of Ably LiveSync with the structured power of Neon Postgres to build a optimistic and scalable comment system in your Next.js application.
Let’s get started by creating a new Next.js project with the following command:
Once that is done, move into the project directory and install the necessary dependencies with the following command:
The libraries installed include:
@ably-labs/models: A library for working with data models and real-time updates in Ably.
@neondatabase/serverless: A serverless Postgres client designed for Neon.
@prisma/adapter-neon: A Prisma adapter for connecting with Neon serverless Postgres.
@prisma/client: Prisma’s auto-generated client for interacting with your database.
ably: A real-time messaging and data synchronization library.
ws: A WebSocket library for Node.js.
The development-specific libraries include:
prisma: A toolkit for Prisma schema management, migrations, and generating clients.
tsx: A fast TypeScript runtime for development and rebuilding.
Once that's done, copy the .env.example to .env via the following command:
Provision a Serverless Postgres
To set up a serverless Postgres, go to the Neon console and create a new project. Once your project is created, you will receive a connection string that you can use to connect to your Neon database. The connection string will look like this:
Replace <user>, <password>, <endpoint_hostname>, <port>, and <dbname> with your specific details.
Use this connection string as an environment variable designated as DATABASE_URL in the .env file.
Database Schema Setup
In the file named schema.tsx, you would see the following code:
The code above defines a function that connects to a Neon serverless Postgres database using a DATABASE_URL environment variable and sets up the necessary schema for the real-time application. It creates two tables, nodes and outbox, to store data and manage message processing, respectively. A trigger function, outbox_notify, is implemented to send notifications using pg_notify whenever new rows are inserted into the outbox table. This ensures the database is ready for real-time updates and WebSocket-based communication.
To run the schema against your Neon Postgres, execute the following command:
If it runs succesfully, you should see Database schema set up succesfully. in the terminal.
Build Reusable React Components and Hooks
1. Typing Effect Animation
To enhance the user experience by simulating real-time interactions, implement a typing effect in the UI to render AI responses incrementally. Create a file named useTypingEffect.ts in the components directory with the following code:
The provided code exports a custom React hook called useTypingEffect. This hook simulates a typing effect for a specified text over a given duration, enhancing the user interface by rendering text incrementally.
2. Conversation Message
To render each message in the conversation history, you need to dynamically indicate whether the message is from the User or the AI. Create a file named Message.tsx in the components directory with the following code:
The code above exports a React component that renders a message. It conditionally displays a Cpu icon for messages from the AI and a User icon for messages from the user, along with the message content.
3. Various States During AI Interaction
Create a file named TextAnimation.tsx in the components directory with the following code:
The code above exports a React component that creates an interactive UI for the AI voice assistant. It utilizes the useTypingEffect hook to simulate a typing effect for the AI's responses and displays different states of interaction, such as "idle," "listening," and "speaking." The component also includes a clickable circle that toggles between starting and stopping the listening state, providing visual feedback through animations.
Generate a Signed URL for private conversations with ElevenLabs
To create a secure access between user and AI (powered by ElevenLabs), create a new file named route.ts in the app/api/i directory with the following code:
The code above defines an API route that generates a signed URL using ElevenLabs API. You will want to use signed URL instead of connecting to a fixed point server so as to allow connection to your personalized, private agents created in ElevenLabs.
Sync Conversations to a Postgres database
Create a file named route.ts in the app/api/c directory with the following code:
The code above defines two endpoint handlers on /api/c:
A POST endpoint that allows you to insert a new message into the messages table. It expects a JSON payload containing the id of the session and the item to be inserted. If the session ID or item is missing, it returns a 400 status code.
A GET endpoint that retrieves all messages associated with a specific session ID. It extracts the session ID from the request URL and queries the messages table, returning the results as a JSON response. If the session ID is not provided, it returns an empty array.
Create the UI for Starting Conversations and Synchronizing Chat History
Create a file named page.tsx in the app/c/[slug] directory with the following code:
The code above does the following:
Defines a loadConversation function which calls the /api/c route to fetch the conversation history based on the particular slug (i.e. the conversation ID).
Uses the useConversation hook by ElevenLabs to display the toast when the instance is connected, and to sync the real-time message to Postgres using the onMessage callback.
Defines a connectConversation function that instantiates a private conversation with the agent after obtaining a signed URL using the /api/i route.
Defines a disconnectConversation function that disconnects the ongoing conversation with the agent.
Creates a useEffect handler which on unmount, ends the ongoing conversation with the agent.
Next, import the TextAnimation component which displays different state of the conversation, whether AI is listening or speaking (and what if so).
Finally, add a Show Transcript button that displays the conversation history stored in Neon to the user.
Now, let's move on to deploying the application to Vercel.
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've just created.
In Settings, update the Environment Variables to match those in your local .env file.
In this guide, you learned how to build a real-time AI voice assistant using ElevenLabs and Next.js, integrating it with a Postgres database to store and retrieve conversation histories. You explored the process of setting up a serverless database, creating a customizable AI agent, and implementing a user-friendly interface with animations and message handling. By the end, you gained hands-on experience connecting various technologies to create a fully functional AI voice assistant application.
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more details, see Getting Support.