This is a sample movie recommendation service that is built on an OpenAI embedding model, Spring AI framework and PostgreSQL pgvector.
The service takes user questions written in plain English and uses a gen AI stack (OpenAI, Spring AI and PostgreSQL pgvector) to provide the user with the most relevant movie recommendations.
- The latest version of Docker and Docker Compose.
- OpenAI API key
If you're planning to run the app on bare metal, then make sure to have:
- Node.js 20+
- Java 21+. Use sdkman to install it within a minute.
- Maven 3.9+
The pgvector extension is supported by a single-server PostgreSQL instance as well as a multi-node YugabyteDB cluster. Feel free to use any of the database options.
-
Create the
postgres-volume
directory for the Postgres container's volume in your home dir. The volume is handy if you'd like to access the logs easily and don't want to lose data when the container is recreated from scratch:mkdir ~/postgres-volume
-
Create a custom Docker network:
docker network create yugaplus-network
-
Start the Postgres container with the pgvector extension:
docker run --name postgres --net yugaplus-network \ -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password \ -p 5432:5432 \ -v ~/postgres-volume/:/var/lib/postgresql/data \ -d ankane/pgvector:latest
-
Make sure the container is running:
docker container ls -f name=postgres
-
Create a custom Docker network:
docker network create yugaplus-network
-
Start a YugabyteDB cluster:
rm -r ~/yugabyte-volume mkdir ~/yugabyte-volume docker run -d --name yugabytedb-node1 --net yugaplus-network \ -p 15433:15433 -p 7001:7000 -p 9001:9000 -p 5433:5433 \ -v ~/yugabyte-volume/node1:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --base_dir=/home/yugabyte/yb_data --daemon=false # Wait until the first node is initialized and ready to accept connection while ! docker exec -it yugabytedb-node1 postgres/bin/pg_isready -U yugabyte -h yugabytedb-node1; do sleep 1; done docker run -d --name yugabytedb-node2 --net yugaplus-network \ -p 15434:15433 -p 7002:7000 -p 9002:9000 -p 5434:5433 \ -v ~/yugabyte-volume/node2:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --join=yugabytedb-node1 --base_dir=/home/yugabyte/yb_data --daemon=false docker run -d --name yugabytedb-node3 --net yugaplus-network \ -p 15435:15433 -p 7003:7000 -p 9003:9000 -p 5435:5433 \ -v ~/yugabyte-volume/node3:/home/yugabyte/yb_data --restart unless-stopped \ yugabytedb/yugabyte:latest \ bin/yugabyted start --join=yugabytedb-node1 --base_dir=/home/yugabyte/yb_data --daemon=false
You have an option of deploying the application in Docker or on your host operating system (bare metal).
By default, the containers will attempt to connect to the PostgreSQL container. If you use YugabyteDB:
-
Update the following settings in the
docker-compose.yaml
:- DB_URL=jdbc:postgresql://yugabytedb-node1:5433/yugabyte - DB_USER=yugabyte - DB_PASSWORD=yugabyte
-
If you use YugabyteDB 2.20.1 or later, then add the following parameter:
- DB_CONN_INIT_SQL="SET yb_silence_advisory_locks_not_supported_error=true"
During the first run, build an image and only then start the containers:
docker-compose up --build
Once the image is ready, use this command to start the containers:
docker-compose up
Start the backend in Docker:
-
Create a Docker image for the backend:
cd backend docker build -t yugaplus-backend .
-
Start a backend container:
For PostgreSQL:
docker run --name yugaplus-backend --net yugaplus-network -p 8080:8080 \ -e DB_URL=jdbc:postgresql://postgres:5432/postgres \ -e DB_USER=postgres \ -e DB_PASSWORD=password \ -e OPENAI_API_KEY=your-api-key \ yugaplus-backend
For YugabyteDB:
docker run --name yugaplus-backend --net yugaplus-network -p 8080:8080 \ -e DB_URL=jdbc:postgresql://yugabytedb-node1:5433/yugabyte \ -e DB_USER=yugabyte \ -e DB_PASSWORD=yugabyte \ -e OPENAI_API_KEY=your-api-key \ yugaplus-backend
Start the frontend in Docker:
-
Create a Docker image for the frontend:
cd frontend docker build -t yugaplus-frontend .
-
Start a frontend container:
docker run --name yugaplus-frontend --net yugaplus-network -p 3000:3000 \ -e REACT_APP_PROXY_URL=http://yugaplus-backend:8080 \ yugaplus-frontend
By default, the backend will attempt to connect to the PostgreSQL container. If you use YugabyteDB:
-
Update the following settings in the
application.properties
file:spring.datasource.url = jdbc:postgresql://yugabytedb-node1:5433/yugabyte spring.datasource.username = yugabyte spring.datasource.password = yugabyte
-
If you use YugabyteDB 2.20.1 or later, then add the following parameter:
spring.datasource.hikari.connection-init-sql="SET yb_silence_advisory_locks_not_supported_error=true"
Start the backend:
-
Provide your OpenAI key in the
backend/src/main/resources/application.properties
file:spring.ai.openai.api-key=sk-
-
Go to the backend directory and start the app:
cd backend mvn spring-boot:run
Start the frontend:
-
Go to the frontend directory:
cd frontend
-
Start the app:
npm install npm start
Go to http://localhost:3000 and test the application.
Sign in using the following credentials:
user1@gmail.com/password
- already has some movies in the libraryuser2@gmail.com/password
- the library is empty
Try a few prompts: A movie about a space adventure. A kids-friendly movie with unexpected ending.