Sandbox: Trivia App
Description of the Trivia Application
This tutorial sets up a trivia app that gives users a set of three daily trivia questions based on some selected preferences. It then allows the user to answer the questions, and save their favorite questions.
The application is deployed online here, if you want a closer look. The user homepage looks like the following:
The full-stack application architecture is set up as the following:
It uses the Minimum Viable Product Stack that we introduce in this course.
The application uses the NextJS framework and uses Supabase for the database and authentication. It also uses the OpenAI API to generate the trivia questions. You’ll learn more when you do the practice exercises for this sandbox!
The setup here will require you to create your own Supabase and OpenAI accounts to get your own API keys. For best security practices, I am not giving out my API keys for OpenAI and Supabase! These set-up steps will help you understand the applcation in a more hands-on way!
Installation Instructions
Prerequisites
You should have set up Node and Git by following the instructions in the Getting Started section.
- Cursor (or your favorite AI-powered IDE)
- Node.js
- Git
- Supabase account
- OpenAI account
MVP Sandbox Trivia App
Step 1: Project Setup
Clone the Github Repository (if you haven’t done this yet)!
git clone https://github.com/karenacai/CurioCity.gitcd CurioCity/Sandbox/trivia-app
Step 2: Supabase Setup
-
Create a Supabase account at supabase.com
-
Create a new project. Don’t worry about this using one of your two free Supabase projects! You can delete the project as soon as you are done with the Sandbox!
-
Note down the API keys for your Supabase project on your project dashboard. You can find this by going to your project dashboard, clicking the ‘Connect’ button, and clicking on the tab ‘App Frameworks’.
-
Set up the Supabase database tables (that are required for this app):
- Go to SQL Editor
- Add the following sql code to the command prompt (and then click Run Query):
This SQL command will set up the database tables required for this project. It also sets up the role-level security properties of the tables (i.e. defines which tables users can access and update).
-- Create trivia_questions tableCREATE TABLE trivia_questions ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, difficulty INTEGER CHECK (difficulty BETWEEN 1 AND 10), question TEXT NOT NULL, choices TEXT[] NOT NULL, answer TEXT NOT NULL, category TEXT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, user_id UUID REFERENCES auth.users(id) NOT NULL);
-- Create user_trivia_history tableCREATE TABLE user_trivia_history ( id SERIAL PRIMARY KEY, user_id UUID REFERENCES auth.users(id) NOT NULL, trivia_id UUID REFERENCES trivia_questions(id) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL);
-- Create favorites tableCREATE TABLE favorites ( id SERIAL PRIMARY KEY, user_id UUID REFERENCES auth.users(id) NOT NULL, trivia_id UUID REFERENCES trivia_questions(id) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, -- Add unique constraint to prevent duplicate favorites UNIQUE(user_id, trivia_id));
-- Create indexes for better query performanceCREATE INDEX idx_trivia_user_id ON trivia_questions(user_id);CREATE INDEX idx_trivia_category ON trivia_questions(category);CREATE INDEX idx_history_user_id ON user_trivia_history(user_id);CREATE INDEX idx_history_trivia_id ON user_trivia_history(trivia_id);CREATE INDEX idx_favorites_user_id ON favorites(user_id);CREATE INDEX idx_favorites_trivia_id ON favorites(trivia_id);
-- Enable Row Level Security (RLS)ALTER TABLE trivia_questions ENABLE ROW LEVEL SECURITY;ALTER TABLE user_trivia_history ENABLE ROW LEVEL SECURITY;ALTER TABLE favorites ENABLE ROW LEVEL SECURITY;
-- Create RLS policiesCREATE POLICY "Users can view all trivia questions" ON trivia_questions FOR SELECT TO authenticated USING (true);
CREATE POLICY "Users can create their own trivia questions" ON trivia_questions FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can view their own history" ON user_trivia_history FOR SELECT TO authenticated USING (auth.uid() = user_id);
CREATE POLICY "Users can create their own history" ON user_trivia_history FOR INSERT TO authenticated WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can view their own favorites" ON favorites FOR SELECT TO authenticated USING (auth.uid() = user_id);
CREATE POLICY "Users can manage their own favorites" ON favorites FOR ALL TO authenticated USING (auth.uid() = user_id);
-- Create users_answers tableCREATE TABLE users_answers ( id SERIAL PRIMARY KEY, user_id UUID REFERENCES auth.users(id) NOT NULL, trivia_id UUID REFERENCES trivia_questions(id) NOT NULL, selected_answer TEXT NOT NULL, is_correct BOOLEAN NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT TIMEZONE('utc'::text, NOW()) NOT NULL, -- Add unique constraint to prevent duplicate answers for the same question UNIQUE(user_id, trivia_id));
-- Enable Row Level SecurityALTER TABLE users_answers ENABLE ROW LEVEL SECURITY;
-- Create policy for inserting - users can only insert their own answersCREATE POLICY users_answers_insert_policy ON users_answers FOR INSERT WITH CHECK (auth.uid() = user_id);
-- Create policy for selecting - users can only view their own answersCREATE POLICY users_answers_select_policy ON users_answers FOR SELECT USING (auth.uid() = user_id);
-- Create policy for updating - users cannot update their answersCREATE POLICY users_answers_update_policy ON users_answers FOR UPDATE USING (false);
-- Create policy for deleting - users cannot delete their answersCREATE POLICY users_answers_delete_policy ON users_answers FOR DELETE USING (false);
-- Add comment to explain table purposeCOMMENT ON TABLE users_answers IS 'Stores user answers to trivia questions';
ALTER TABLE trivia_questions ENABLE ROW LEVEL SECURITY;ALTER TABLE user_trivia_history ENABLE ROW LEVEL SECURITY;
-- Create policy for inserting trivia_questions (rate limit to 3 per day)CREATE POLICY "Rate limit trivia question creation"ON trivia_questionsFOR INSERTTO authenticatedWITH CHECK ( -- Check if user has created fewer than 3 questions in the last 24 hours ( SELECT COUNT(*) FROM trivia_questions WHERE user_id = auth.uid() AND created_at > (CURRENT_TIMESTAMP - INTERVAL '1 day') ) < 3);
-- Create policy for inserting user_trivia_history (rate limit to 3 per day)CREATE POLICY "Rate limit trivia history creation"ON user_trivia_historyFOR INSERTTO authenticatedWITH CHECK ( -- Check if user has created fewer than 3 history entries in the last 24 hours ( SELECT COUNT(*) FROM user_trivia_history WHERE user_id = auth.uid() AND created_at > (CURRENT_TIMESTAMP - INTERVAL '1 day') ) < 3);
-
Verify the table was created successfully and looks like the following. To access the Schema Visualizer, click Databases -> Schema Visualizer, and you should see something like:
-
You will also need to update the URL properties in your account. To access the URL configuration settings, go to Authentication -> URL Configuration. This configuration is meant to help your application know which URL to redirect the user to when they do certain parts of the user authentication process (like password confirmation or resetting their passwords).
Step 3: OpenAI Setup
- Create an OpenAI account at openai.com
- Navigate to API settings (or go to this link) and generate an API key.
- Add the API key to your
.env
file (as the OPEN_API_KEY variable)
Step 4: Environment Setup
Open up your favorite IDE (we recommend Cursor), and set up the .env file for the project.
- Create a
.env
file in the root directory of this project. - Add your API keys (format provided below)
NEXT_PUBLIC_SUPABASE_URL=your_supabase_urlNEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_keyOPENAI_API_KEY=your_openai_api_key
Step 5: Installing Project Dependencies
# Install project dependenciesnpm install
Step 6: Launch the Application
You will want to
npm run dev
Note, you will have to run this command in the root of your project folder, where the package.json file is located.
Learning Challenges
Phase 0: Playing around with the app
-
Run the application with the command ‘npm run dev’. Explore the application by clicking on buttons and trying out the different features. *Note, every time you generate trivia questions, you will be spending some of your OpenAI credits (money).
a. Authentication:
- Go through the process of signing up for an account. Check your Supabase Users table (in your web browser) to see if the sign up was successful!
- Now that you have created an account, go ahead and sign in.
- Try resetting your user account password. Did that work? If not, what happened?
b. Trivia question generation:
- Generate a set of trivia questions! What happens?
- Can you check that the trivia_questions table and the user_trivia_questions table were updated in Supabase?
- What happens if you try to generate trivia questions again?
c. Favorites page:
- Favorite one or two of the questions on the home page.
- Do they appear in the favorites table in Supabase?
- Check the favorites page and see if the questions show up.
- Does the unfavoriting functionality work?
d. History page:
- Check the history page, and make sure the user’s past history questions appear.
Phase 1: Coding with AI open-exploration
-
(Feature 1: Cosmetic Change) Can you get Cursor to increase the text size of the trivia category buttons?
a. What learning/research/discovery did you do prior to writing and implementing a Cursor prompt (with LLMs and/or looking )?
b. Which files did Cursor edit/modify to implement this feature? Where does this file live?
c. What are some bugs, issues, or limitations you encountered during this process?
d. Were you successful in implementing the feature? If so, show a screenshot or GIF of it working.
-
(Feature 2: Countdown Timer) Add a timer that counts down as soon as the user clicks the button to generate trivia questions. As soon as the timer expires, can you have a UI/UX display that says ‘your time has run out’, and prevent the user from answering questions?
a. What learning/research/discovery did you do prior to writing and implementing a Cursor prompt (with LLMs and/or looking )?
b. Which files did Cursor edit/modify to implement this feature? Where do these files live?
c. What are some bugs, issues, or limitations you encountered during this process?
d. Were you successful in implementing the feature? If so, show a screenshot or GIF of it working.
Sandbox Feature Tips
-
(Feature 3: Prompt Modification) Modifying the prompt to take in additional user preference (for example, add a field for spiciness, goofiness, mind-bendiness, or whatever you can think of). Make sure that the prompt to the OpenAI API takes this user input into consideration when generating the trivia questions.
a. What learning/research/discovery did you do prior to writing and implementing a Cursor prompt (with LLMs and/or looking )?
b. Which files did Cursor edit/modify to implement this feature? Where do these files live?
c. What are some bugs, issues, or limitations you encountered during this process?
d. Were you successful in implementing the feature? If so, show a screenshot or GIF of it working.
Phase 2: Coding with AI directed exploration (to learn about project structure/files)
-
Can you find out where the UI/UX for the /sign-in and /sign-up forms live? You can either inspect the files/folders manually or use Cursor for guidance if you need help!
a. What do you notice about the route (i.e. website.com/sign-in) and where the file lives in the code repository? What is the pattern?
-
You have gone through the process of signing up for an account. Can you get Cursor to help you map where in the code this whole user-flow is encoded? Can you draw out a diagram to show what code files/functions are hit?
-
Let’s explore the OpenAI API integration.
a. What code is being called between the user hitting the ‘Generate Trivia Questions’ button and the questions being generated and appearing on the page?
b. Where is the route where the OpenAI API is called? What is happening in this code file? Can you walk through and describe the general functionality.
-
Can you find and list all instances where the Supabase tables are being updated or modified?
a. For each of these instances, can you label whether the Supabase client is being called on the server or client-side and why?
b. What is the difference between server vs. client-side?
Reflections on Coding with AI
-
What are your initial thoughts about coding with AI? Any surprises? Challenges?
-
When did you feel like you were collaborating with AI vs. just receiving instructions from it?
-
How helpful was AI in resolving bugs or identifying problems?
-
Did you ever get stuck in trying to fix a problem? Or in an infinite cycle of errors? What happened and what did you try?
-
Did you notice your prompts improving over time? How did your approach evolve?
-
On a scale of 1-10, how confident are you in knowing which files should change if I asked you to implement a specific feature (like the ones in phase 1 of the exercise)?