Beyond the Binge: Building Smart Movie & TV Recommendations with Angular, Genkit & Firebase
The Dawn of Personalized Entertainment: Building an AI-Powered Recommender
Ever wondered how streaming platforms magically suggest the perfect movie or TV show, making it seem like they know your tastes better than you do? Today, we're pulling back the curtain on that magic! We're diving into the exciting world of building an intelligent recommendation engine for movies and TV shows, designed to understand your unique preferences and offer truly personalized viewing suggestions.
This journey will take us through leveraging the power of Firebase, the rich data of the TMDB API, and the cutting-edge capabilities of Google's Gemini models orchestrated by the Genkit framework. We'll explore a practical, secure, and scalable architectural approach, showcasing how these powerful technologies integrate seamlessly to enhance user engagement and transform your application into a truly smart entertainment hub.
An AI-powered recommendation engine, powered by Genkit
Our fictional movie and TV shows hub, “Nova Reel”, is an Angular-based application for browsing, discovering, and getting personalized recommendations for movies and TV shows. It leverages Google's Genkit AI platform to provide intelligent recommendations based on user favorites.
Nova Reel app: https://nova-reels.web.app/
GitHub Repo: https://github.com/waynegakuo/nova-reel
Screenshot of the Nova Reel web application
Key Features
🔥 Browse trending movies and TV shows
🔍 Filter content by categories (Popular, Top Rated, Now Playing, etc.)
🔎 Search for specific titles
ℹ️ View detailed information about movies and TV shows
⭐ Save favorites for quick access
🤖 Get AI-powered personalized recommendations based on your favorites
📱 Responsive design for all devices
🏗️ Architecture
Nova Reel is built with a modern tech stack that combines frontend and backend technologies:
🖥️ Frontend
🅰️ Angular: The frontend is built with Angular, using standalone components and signals for reactive state management
🔐 Firebase Authentication: For user authentication and management
🗄️ Firebase Firestore: For storing user favorites and preferences
🔥 Angular Fire: For integrating Firebase services with Angular
☁️ Backend
⚡ Firebase Functions: Serverless backend functions that handle API requests and AI processing
🧠 Genkit: Google's AI platform for building generative AI applications
💫 Gemini 2.5 Pro: The underlying AI model used for generating recommendations
🎞️ TMDB API: External API for fetching movie and TV show data
🤖 AI Recommendation Engine
The heart of Nova Reel is its AI recommendation engine, which uses Genkit to analyze user favorites and generate personalized recommendations:
📊 Data Collection: The app stores user favorites in Firebase Firestore
🧠 AI Processing: When a user requests recommendations, the app calls a Firebase Function that uses Genkit to:
📥 Fetch the user's favorites from Firestore
🔄 Create a context from these favorites to inform the AI
💫 Use the Gemini 2.5 Pro model to generate personalized recommendations
💡 Provide reasoning for the recommendations
📱 Recommendation Display: The frontend displays these recommendations in the "For You" tab, along with the AI's reasoning
Why use Genkit?
Smart Data Integration (Retrieval Augmented Generation—RAG): the AI fetches your favorites from Firestore, then dynamically queries TMDB as it “thinks” using our
getTmdbDataTool
. This grounds the AI in your real data, leading to highly relevant recommendations.AI-Driven Actions (Tool Calling): Our AI uses the
getTmdbDataTool
to fetch data from TMDB. It’s like giving the AI its own web browser and search engine. This empowers the AI to reason and retrieve information for better suggestions.Robust Backend & Security: Our AI logic runs on Firebase Cloud Functions.
Secure API Keys: TMBD & Gemini keys stay safely hidden on the server.
Complex logic offloaded: Keeps the heavy lifting off the user’s device.
Debugging & Observability: Genkit comes with a Developer UI and Firestore tracing. We can literally see:
What prompt did the AI receive?
What “tools” it decided to call.
The exact data returned by those tools.
The AI’s final reasoning.
NB: This guide assumes you have Angular CLI (v19 or later), Node.js (v20+), npm, and Git installed. If not, kindly check out the GitHub Repo for the requirements for this project.
🔥 Firebase Project Setup & TMDB API Key
🏗️ Create a Firebase Project:
Go to Firebase Console.
Click "Add project" and follow the prompts to create your project.
⚠️ Important: Upgrade your project to the Blaze (pay-as-you-go) plan. Cloud Functions and Vertex AI (which Genkit uses) require a billing-enabled project. Don't worry, free tiers are generous for testing.
☁️ Enable Essential Google Cloud APIs
Your Firebase project uses Google Cloud behind the scenes. For secure secret management, the Secret Manager API must be enabled. Other necessary APIs (like Cloud Functions, Cloud Build, Cloud Run, Vertex AI) are usually enabled automatically by Firebase when you deploy functions or use AI features.
Go to the Google Cloud Console for your Firebase project.
In the navigation menu, go to APIs & Services > Enabled APIs & Services.
Click on +Enable APIs and services.
Search for and enable the Secret Manager API.
🛠️ Install Firebase CLI:
Note for Firebase Studio users: Skip step 2 (npm install command) and go directly to step 3 (firebase login).
Open your terminal/command prompt.
Install the Firebase CLI globally:
npm install -g firebase-tools
Log in to Firebase:
firebase login
🚀 Initialize Firebase in Your Project:
Navigate to your project's root directory (you should already be there after cloning the repository).
Initialize Firebase:
firebase init
Select "Functions" and "Firestore" when prompted.
Choose your existing Firebase project to link to.
Select TypeScript for functions (highly recommended).
For Firestore, accept the default rules file (you can change it later).
Do NOT overwrite existing files if prompted.
🔄 Update the .firebaserc File:
You can update the .firebaserc file in two ways:
Option 1: Using Firebase CLI (Recommended)
Run the following command to set your Firebase project ID:
firebase use YOUR_PROJECT_ID
Replace YOUR_PROJECT_ID
with the project ID of the Firebase project you created. This command will automatically update your .firebaserc file.
Option 2: Manual Editing
Open the
.firebaserc
file in your project root directory.Replace the default project name with your Firebase project ID:
{
"projects": {
"default": "YOUR_PROJECT_ID"
}
}
Replace YOUR_PROJECT_ID
with the project ID of the Firebase project you created.
🔥 Add Firebase Configuration to Your Project:
Go to your Firebase project in the Firebase Console.
Click on the gear icon (⚙️) next to "Project Overview" and select "Project settings".
Scroll down to the "Your apps" section and select your web app (or create one if you haven't already).
Under the "SDK setup and configuration" section, select "Config" to view your Firebase configuration object.
Copy the configuration object that looks like this:
{
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT_ID.appspot.com",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
}
Open the environment files in your project:
For production:
src/environments/environment.ts
For development:
src/environments/environment.development.ts
Replace the existing
firebaseConfig
object with your own Firebase configuration:
export const environment = {
production: true, // or false for environment.development.ts
firebaseConfig: {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT_ID.appspot.com",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID",
measurementId: "YOUR_MEASUREMENT_ID"
}
};
🎬 Get TMDB API Key:
Go to TMDB.
Sign up or log in.
Go to your user profile (click your avatar) -> Settings -> API.
Request a new API key (Developer/v3).
Note down your API Read Access Token (Bearer Token). It starts with "eyJ...".
🔐 Set TMDB API Key as Firebase Secret:
Navigate to your functions directory:
cd functions
Set the TMDB API key as a Firebase secret:
firebase functions:secrets:set TMDB_API_BEARER_TOKEN
Paste your TMDB Bearer Token when prompted.
Return to the project root:
cd ..
🔑 API Keys and Deployment
🔑 Set up your API keys:
Create a
.env
file in thefunctions
directory with your Gemini API key (for local development):
cd functions
echo "GEMINI_API_KEY=your_gemini_api_key" > .env
cd ..
Set up the Gemini API key as a Firebase secret (for production deployment):
firebase functions:secrets:set GEMINI_API_KEY
Note: When running this command, you'll be prompted to enter the actual secret value. The GEMINI_API_KEY is needed both as an environment variable (for local development) and as a Firebase secret (for production deployment).
🔥 Configure Firebase:
firebase use your-project-id
🚀 Deploy Firebase Functions:
firebase deploy --only functions
🏃♂️ Run the application locally:
ng serve
🧠 Building an AI Recommendation Engine with Genkit
Nova Reel demonstrates how to build an AI recommendation engine using Genkit. Here's how it works:
1️⃣ Setting Up Genkit
In the Firebase Functions (functions/src/index.ts
), Genkit is configured with the Gemini model:
// Configure Genkit
const ai = genkit({
plugins: [
googleAI({apiKey: process.env.GEMINI_API_KEY }),
],
model: googleAI.model('gemini-2.5-pro'), // Specify your Gemini model
});
2️⃣ Creating a Custom Tool for TMDB Data
A custom tool is defined to allow the AI to fetch movie and TV show data from TMDB:
export const getTmdbDataTool = ai.defineTool(
{
name: 'getTmdbData',
description: 'Fetches movie or TV show data from TMDB using specific endpoint, ID, or query.',
inputSchema: z.object({
endpoint: z.string().describe('TMDB API endpoint (e.g., "movie", "tv", "search/movie")'),
id: z.number().optional().describe('ID for specific movie/TV show'),
query: z.string().optional().describe('Search query string'),
}),
outputSchema: z.any().describe('JSON data from TMDB API response'),
},
async ({ endpoint, id, query }) => {
const url = constructTmdbUrl(endpoint, { id, query });
return await executeTmdbRequest(url, 'getTmdbDataTool');
}
);
3️⃣ Defining the Recommendation Flow
A Genkit flow is defined to handle the recommendation process:
export const _getRecommendationsFlowLogic = ai.defineFlow(
{
name: 'getRecommendationsFlow',
inputSchema: RecommendationInputSchema,
outputSchema: RecommendationOutputSchema,
},
async (input) => {
// Fetch user's favorite movies/tv shows from Firestore
const userFavoritesRef = db.collection('users').doc(input.userId).collection('favorites');
const favoritesSnapshot = await userFavoritesRef.get();
const favoriteItems = favoritesSnapshot.docs.map(doc => doc.data());
// Prepare context for the AI from user favorites
let favoritesContext = favoriteItems.map(item =>
`${item['type'] === 'movie' ? 'Movie' : 'TV Show'}: ${item['title']} (TMDB ID: ${item['tmdbId']})`
).join('; ');
// Generate recommendations using the AI model
const { output } = await ai.generate({
tools: [getTmdbDataTool], // Make the TMDB data tool available to the model
prompt: `
You are a highly intelligent movie and TV show recommendation assistant.
The user has a list of favorite movies and TV shows.
User's favorites: ${favoritesContext}
Based on these favorites, recommend ${input.count} additional movies or TV shows that the user might enjoy.
Consider their genres, actors, directors, themes, and overall vibe.
Prioritize items with high TMDB ratings.
Avoid recommending any of the items already in the user's favorites list.
For each recommendation, provide the title, whether it's a "movie" or "tv" show, its TMDB ID,
a brief overview, and its poster path.
Explain your reasoning briefly after the recommendations.
`,
output: {
format: 'json',
schema: RecommendationOutputSchema,
},
});
return output || { recommendations: [], reasoning: 'Unable to generate recommendations.' };
}
);
4️⃣ Exposing the Flow as a Firebase Function
The flow is exposed as a callable Firebase Function:
export const getRecommendationsFlow = onCallGenkit(
{
secrets: [TMDB_BEARER_TOKEN],
region: 'africa-south1',
cors: true,
},
_getRecommendationsFlowLogic
);
5️⃣ Calling the Function from the Frontend
In the frontend (src/app/services/media/media.service.ts
), the function is called to get recommendations:
async getAiRecommendationsData(count: number = 5): Promise<AiRecommendationResponse> {
const userId = this.authService.getUserId();
if (!userId) {
throw new Error('User must be authenticated to get AI recommendations');
}
const callableGetRecommendations = httpsCallable(this.functions, 'getRecommendationsFlow');
try {
const result = await callableGetRecommendations({
userId: userId,
count: count
});
return result.data as AiRecommendationResponse;
} catch (error: any) {
console.error('Error fetching AI recommendations from Cloud Function:', error);
throw error;
}
}
6️⃣ Displaying Recommendations
The recommendations are displayed in the "For You" tab of the landing page, along with the AI's reasoning for the recommendations.
📝 Usage
🔍 Browse Content: Use the "Movies" and "TV Shows" tabs to browse trending content
ℹ️ View Details: Click on any movie or TV show to view detailed information
⭐ Add to Favorites: Click the "Add to Favorites" button on any movie or TV show detail page
🤖 Get Recommendations: Navigate to the "For You" tab to see personalized recommendations based on your favorites
🔄 Refresh Recommendations: Click the "Refresh Recommendations" button to get new recommendations
Next Steps: A Series of Smart AI Features
This recommendation engine is just the beginning. Our architecture, built on Genkit, provides a flexible foundation for a series of new AI-powered features. As a next step, we will continue this journey by:
"Guess the Movie" from a Screenshot: We'll build a feature that allows users to upload a screenshot of a movie scene. Our AI agent, using a vision-enabled Gemini model, will analyze the image to identify the title, which will then be verified with the TMDB API to provide accurate information.
Mood-Based Recommendations: We'll introduce a feature that lets users dictate their mood (e.g., "I want a thrilling, suspenseful movie") in natural language. Our AI will understand the sentiment and keywords to recommend movies that match the user's emotional state, moving beyond simple genre filtering.
An Interactive Chatbot: We will create a conversational agent that can handle multi-turn requests. Users can ask questions like "Who directed this movie?" or give commands like "Add this to my watchlist," and the AI will use its tools to perform these actions in a conversational manner.
By adding these features, we can turn a simple recommendation app into a much more dynamic and user-friendly experience, with Genkit acting as the intelligent layer that makes it all possible. This approach demonstrates how AI can be a central part of an application's architecture, not just a supporting tool.
Conclusion
Personalized recommendation engines have evolved from a novelty to an essential part of the modern digital experience. This article has showcased a practical approach to building a robust and intelligent recommender system for movies and TV shows. By leveraging Firebase Cloud Functions, we can securely handle API keys and offload complex logic from the frontend. Using Firestore, we efficiently store and retrieve user-specific data, forming the basis for personalization. Finally, by integrating the Genkit framework, we empower the Google Gemini model to act as a data-aware AI agent.
This architecture allows the AI to autonomously "reason" and "act" by calling a tool that connects to the TMDB API, enabling it to fetch real-time data to ground its recommendations. This multi-step, server-side process ensures the suggestions are not only personalized but also accurate and reliable. The result is a seamless and engaging user experience that goes beyond simple content matching.
The ongoing advancements in AI promise even greater innovation in this field, with future systems likely to incorporate more explainable AI for transparency, federated learning for privacy, and real-time adaptations to user behavior. The pursuit of creating a frictionless and truly tailored entertainment experience will continue to be a driving force, with AI playing an increasingly central role.