Building a Full-Stack Portfolio Website with a RAG Powered Chatbot
Develop a modern React portfolio website featuring a chatbot powered by pgvector, Neon Postgres, FastAPI, and OpenAI.
In this guide, you will build a full-stack portfolio website using React
for the frontend and FastAPI
for the backend, featuring a Retrieval-Augmented Generation (RAG) chatbot that leverages pgvector
on Neon
's serverless Postgres to store and retrieve embeddings created with OpenAI
's embedding model.
This project is perfect for showcasing technical skills through a portfolio that not only demonstrates front-end and back-end capabilities but also integrates AI to answer questions about your experience.
Prerequisites
Before you start, ensure that you have the following tools and services ready:
pip
: This is required for installing and managing Python packages, including uv for creating virtual environments. You can check ifpip
is installed by running the following command:- Neon serverless Postgres : You will need a Neon account for provisioning and scaling your
PostgreSQL
database. If you don't have an account yet, sign up here. - Node.js: Needed for developing the frontend using React, you can download it following the official installation guide.
- OpenAI API key: You need access to the OpenAI API for generating embeddings, you can sign up here.
Setting up the Backend
Follow these steps to set up your backend for the full-stack portfolio website:
-
Create the project structure.
Since this is a full-stack project, your backend and frontend will be in separate directories within a single parent folder. Begin by creating the parent folder and moving into it
-
Create a
uv
Python virtual environment.If you don't already have uv installed, you can install it with:
Once
uv
is installed, create a new project:This will create a new project directory called
portfolio_backend
. Open this directory in your code editor of your choice. -
Set up the virtual environment.
You will now create and activate a virtual environment in which your project's dependencies will be installed.
You should see
(portfolio_backend)
in your terminal now, this means that your virtual environment is activated. -
Install dependencies.
Next, add all the necessary dependencies for your project:
where each package does the following:
FastAPI
: A Web / API frameworkAsyncPG
: An asynchronous PostgreSQL clientUvicorn
: An ASGI server for our appLoguru
: A logging libraryPython-dotenv
: To load environment variables from a .env fileopenai
: The OpenAI API client for generating embeddings and chatbot responsespgvector
: A Python client for working with pgvector in PostgreSQL
-
Create the project structure.
Create the following directory structure to organize your project files:
Setting up your Database
In this section, you will set up the pgvector
extension using Neon's console, add the database's schema, and create the database connection pool and lifecycle management logic in FastAPI.
First, add pgvector to Postgres:
Next, add the schema to your database:
This table will store the embeddings generated by OpenAI for the chatbot responses. The content
column will store the text for which the embedding was generated, and the embedding
column will store the 1536-dimensional vector. An embedding is a representation of text in a high-dimensional space that captures the meaning of the text, allowing you to compare and search for similar text.
With your schema in place, you're now ready to connect to your database in the FastAPI application. To do this you must create a .env
file in the root of the project to hold environment-specific variables, such as the connection string to your Neon PostgreSQL database, and API keys.
Make sure to replace the placeholders (user, password, your-neon-hostname, etc.) with your actual Neon database credentials, which are available in the console, and to fetch the OpenAI key from the OpenAI console.
In your project, the database.py
file manages the connection to PostgreSQL
using asyncpg
and its connection pool, which is a mechanism for managing and reusing database connections efficiently. With this, you can use asynchronous queries, allowing the application to handle multiple requests concurrently.
init_postgres
is responsible for opening the connection pool to the PostgreSQL
database and close_postgres
is responsible for gracefully closing all connections in the pool when the FastAPI
app shuts down to properly manage the lifecycle of the database.
Throughout your API you will also need access to the pool to get connection instances and run queries. get_postgres
returns the active connection pool. If the pool is not initialized, an error is raised.
Defining the Pydantic Models
Now, you will create the models that represent the data structures used in your API. These models will be used to validate the request and response data in your API endpoints. The API will use these models to serialize and deserialize data between the client and the server. For your API, you will need to complete a chat request and add embeddings to the database.
Each of the models represent the following:
PortfolioEntryCreate
: Represents the input for creating a new embeddingPortfolioEntryResponse
: Represents the output of a created embeddingQueryRequest
: Represents a question to ask the chatbotQueryResponse
: Represents the response from the chatbot
Creating the API Endpoints
In this section, you will create the API routes for adding new portfolio entries and chatting with the chatbot. The add-portfolio-entry
route will add a new portfolio entry to the database and store its embedding. The chat
route will use the stored embeddings to generate a response to a user query.
A typical RAG chatbot workflow involves first creating an embedding for the user query, then finding the most similar embeddings in the database, and finally using the context of these embeddings to generate a response. Here, embeddings are generated using OpenAI's text-embedding-3-small model, and the similarity between embeddings is calculated using the <=>
operator in PostgreSQL, which represents the cosine similarity between two vectors. The chatbot response is then generated using OpenAI's GPT-4o-mini model.
In the chat
route, the chatbot is sent the text obtained from RAG and the user questions, but is also sent a system message that sets the context for the chatbot. You can customize this message to provide additional context or instructions to the chatbot, and to guide the chatbot's responses to your liking.
Running the Application
After setting up the database, models, and API routes, the next step is to run the FastAPI
application.
The main.py
file defines the FastAPI
application, manages the database lifecycle, and includes the routes you created above.
Since you will be connecting your application from the frontend, you will need to allow CORS (Cross Origin Resource Sharing). The CORSMiddleware
is added such that the API can accept requests from any origin, including your React app.
To run the application, use uvicorn CLI with the following command:
Adding Embeddings to the Database
You can now add embeddings to the database by sending a POST request to the /add-entry
endpoint with the content you want to store. To do this, you can use a tool like curl
,Postman
, httpie
, or any other HTTP client. For example, using httpie
:
For the best results, you should add a variety of portfolio entries that cover different aspects of your experience and skills. This will help the chatbot generate more accurate responses to user queries.
Testing the Chatbot
Using httpie
, you can test the chatbot by sending a POST request to the /chat
endpoint with a user query. For example:
The chatbot will respond with a relevant answer based on the embeddings stored in the database, something like:
Setting up the Frontend
Now that the backend is set up and running, it's time to set up the frontend using React. The frontend will be responsible for interacting with the FastAPI backend and displaying your portfolio, as well as allowing users to ask questions to the RAG-powered chatbot.
-
Clone the frontend repository.
First, go back to the parent directory (portfolio_project) you created at the beginning of this guide and clone the frontend repository:
Then, open up that new directory in your code editor.
-
Install the dependencies.
Once you have cloned the frontend repository, install the necessary dependencies using
npm
:This will install all the packages specified in the
package.json
file, which are required for the React app to run. -
Update the frontend content.
Now, you will update the content of your portfolio, such as your bio, projects, and skills, and experience to match your personal details. Each of the section in the portfolio is a separate component that you can modify to include your own information. These include:
- Landing: The landing page of the portfolio, which includes your name, bio, and a profile picture.
- Experience: A section that lists your work experience, including the company name, logo, your position, and a brief description of your role.
- Skills: A section that lists your technical skills, such as programming languages, frameworks, and tools you are proficient in. Find the logos of your technologies at Devicon
- Projects: A section that lists your projects, including the project name, description, and a link to the project's GitHub repository or live demo.
The chatbot component is responsible for sending user queries to the backend and displaying the chatbot responses, and can be found in the
Chatbot.tsx
file.
Running the Frontend
Once you've updated the content, you can start the frontend development server to preview your portfolio website.
To start the development server, run the following command:
This will start the React development server at http://localhost:3000. Open your browser and navigate to that address to view your portfolio website.
Now, with the React app running, take a look at how the website appears. Ensure that the design, content, and overall presentation are what you expect. You can interact with the chatbot (which will not yet be functional until the backend is also running) to check the layout and form submission.
To test the chatbot functionality, you will need to have the backend running as well. To do this, open another console window, navigate to the portfolio_backend
directory, and run the FastAPI application using the uvicorn
command as shown earlier.
Once the API is running, you can interact with the chatbot on the frontend to test the chatbot functionality, try out some prompts, tweak the responses, and see how the chatbot performs.
Optional: Running the Full-Stack Project Using Docker and Docker Compose
In this optional section, you will containerize both the frontend and backend of your full-stack portfolio website using Docker. By using Docker, you can ensure that your application runs consistently across different environments without worrying about dependencies and setups. Docker Compose will allow you to orchestrate running both services (frontend and backend) with a single command.
The only prerequisite for this section is having Docker installed on your machine. If you don't have it installed, you can follow the Docker installation guide.
-
Dockerize the backend.
To Dockerize the backend, you will create a
Dockerfile
in theportfolio_backend
directory. This file will define the steps to build the Docker image for your FastAPI application.This Dockerfile:
- Copies your FastAPI app into the container
- Installs all necessary Python dependencies
- Exposes port 8000, which is where FastAPI will run
- Runs the FastAPI app using uvicorn
-
Dockerize the frontend.
Dockerizing the frontend is similar to the backend. Create a
Dockerfile
in the root of theportfolio_frontend
directory:This Dockerfile:
- Uses Node.js to install frontend dependencies
- Builds the React application
- Serves the static build using the serve package
- Exposes port 3000 where the React app will be available
-
Set up Docker Compose.
Docker Compose simplifies the process of running multiple containers together. You can define both the frontend and backend in a single configuration and run them together with a single command.
Below is the
docker-compose.yml
file, placed at the root of the project, which sets up both the services: -
Run the application with Docker Compose.
Once your Dockerfiles and Docker Compose file are ready, you can bring up the entire stack using:
This command will:
- Build the Docker images for both the backend and frontend
- Start both the FastAPI backend (on port 8000) and the React frontend (on port 3000)
- Automatically manage the service dependencies (the frontend will wait until the backend is up before starting)
Using Docker and Docker Compose to run your full-stack portfolio website simplifies the process of managing dependencies and ensures consistency across different environments. You can now run your entire application, both frontend and backend, in isolated containers with a single command. This setup is also beneficial if you plan to deploy your application to production in a cloud environment or if you want to share the project with others who can run it without manual installation steps.
Conclusion
You have successfully built and deployed a full-stack portfolio website powered by React, FastAPI, pgvector, Neon Postgres, and OpenAI. By leveraging OpenAI embeddings and the RAG-powered chatbot, you added an AI-driven layer to your portfolio that can dynamically answer questions about your projects, skills, and experience.
The next steps in the project could include deploying the application to a cloud platform like AWS, Azure, or Google Cloud, adding more features to the chatbot, or customizing the frontend design to match your personal style. You can also extend the chatbot's capabilities by fine-tuning it on more data or using a different model for generating responses.