1. Before you begin
In this codelab, you'll learn how to integrate Firebase with a Next.js web app called Friendly Eats, which is a website for restaurant reviews.

The completed web app offers useful features that demonstrate how Firebase can help you build Next.js apps. These features include the following:
- Automatic build and deploy: This codelab uses Firebase App Hosting to automatically build and deploy your Next.js code every time you push to a configured branch.
- Sign-in and sign-out: The completed web app lets you sign in with Google and sign out. User login and persistence is managed entirely through Firebase Authentication.
- Images: The completed web app lets signed-in users upload restaurant images. Image assets are stored in Cloud Storage for Firebase. The Firebase JavaScript SDK provides a public URL to uploaded images. This public URL is then stored in the relevant restaurant document in Cloud Firestore.
- Reviews: The completed web app lets signed-in users post reviews of restaurants that consist of a star rating and a text-based message. Review information is stored in Cloud Firestore.
- Filters: The completed web app lets signed-in users filter the list of restaurants based on category, location, and price. You can also customize the sorting method used. Data is accessed from Cloud Firestore, and Firestore queries are applied based on the filters used.
Prerequisites
- A GitHub account
- Knowledge of Next.js and JavaScript
What you'll learn
- How to use Firebase with the Next.js App Router and server-side rendering.
- How to persist images in Cloud Storage for Firebase.
- How to read and write data in a Cloud Firestore database.
- How to use sign-in with Google with the Firebase JavaScript SDK.
What you'll need
- Git
- A recent stable version of Node.js
- A browser of your choice, such as Google Chrome
- A development environment with a code editor and terminal
- A Google account for the creation and management of your Firebase project
- The ability to upgrade your Firebase project to the Blaze pricing plan
2. Set up your development environment and GitHub repository
This codelab provides the app's starter codebase and relies on the Firebase CLI.
Create a GitHub repository
The codelab source can be found at https://github.com/firebase/friendlyeats-web. The repository contains sample projects for multiple platforms. However, this codelab uses only the nextjs-start directory. Take note of the following directories:
* `nextjs-start`: contains the starter code upon which you build.
* `nextjs-end`: contains the solution code for the finished web app.
Copy the nextjs-start folder into your own repository:
- Using a terminal, create a new folder on your computer and change into the new directory:
mkdir codelab-friendlyeats-web cd codelab-friendlyeats-web - Use the giget npm package to fetch only the
nextjs-startfolder:npx giget@latest "gh:firebase/friendlyeats-web/nextjs-start#master" . --install - Track changes locally with git:
git init git add . git commit -m "Codelab starting point" git branch -M main - Create a new GitHub repository: https://github.com/new. Name it anything you'd like.
- Depending on how you authenticate to GitHub (HTTPS or SSH), copy the new URL that GitHub creates for you:
https://github.com/<USER_NAME>/<REPOSITORY_NAME>.gitorgit@github.com:<USER_NAME>/<REPOSITORY_NAME>.git
- Push local changes to your new GitHub repository by running the following command. Substitute your actual repository URL for the
<REPOSITORY_URL>placeholder.git remote add origin <REPOSITORY_URL> git push -u origin main - You should now see the starter code in your GitHub repository.
Install or update the Firebase CLI
Run the following command to verify that you have the Firebase CLI installed and that it's v14.1.0 or higher:
firebase --version
If you see a lower version or you don't have the Firebase CLI installed, run the install command:
npm install -g firebase-tools@latest
If you're unable to install the Firebase CLI because of permission errors, see the npm documentation or use another installation option.
Log in to Firebase
- Run the following command to log in to the Firebase CLI:
firebase login
- Depending on whether you want Firebase to collect data, enter
YorN. - In your browser, select your Google account, and then click Allow.
3. Set up your Firebase project
In this section, you'll set up a Firebase project and associate a Firebase web app with it. You'll also set up the Firebase services used by the sample web app.
Create a Firebase project
- Sign into the Firebase console using the same Google Account you used in the previous step.
- Click the button to create a new project, and then enter a project name (for example,
FriendlyEats Codelab).
- Click Continue.
- If prompted, review and accept the Firebase terms, and then click Continue.
- (Optional) Enable AI assistance in the Firebase console (called "Gemini in Firebase").
- For this codelab, you do not need Google Analytics, so toggle off the Google Analytics option.
- Click Create project, wait for your project to provision, and then click Continue.
Upgrade your Firebase pricing plan
To use Firebase App Hosting and Cloud Storage for Firebase, your Firebase project needs to be on the pay-as-you go (Blaze) pricing plan, which means it's linked to a Cloud Billing account.
- A Cloud Billing account requires a payment method, like a credit card.
- If you're new to Firebase and Google Cloud, check if you're eligible for a $300 credit and a Free Trial Cloud Billing account.
- If you're doing this codelab as part of an event, ask your organizer if there are any Cloud credits available.
To upgrade your project to the Blaze plan, follow these steps:
- In the Firebase console, select to upgrade your plan.
- Select the Blaze plan. Follow the on-screen instructions to link a Cloud Billing account to your project.
If you needed to create a Cloud Billing account as part of this upgrade, you might need to navigate back to the upgrade flow in the Firebase console to complete the upgrade.
Add a web app to your Firebase project
- Navigate to your Project overview in your Firebase project, click Add app, and then click Web.
- In the App nickname text box, enter a memorable app nickname, such as
My Next.js app. - Keep the Also set up Firebase Hosting for this app checkbox unchecked.
- Click Register app > Continue to console.
Set up Firebase services in the Firebase console
Set up Authentication
- In the left-panel of the Firebase console, expand Build and then select Authentication.
- Click Get started.
- In the Sign-in providers column, click Google > Enable.
- In the Public-facing name for project text box, enter a memorable name, such as
My Next.js app. - From the Support email for project drop-down, select your email address.
- Click Save.
Set up Cloud Firestore
- In the left-panel of the Firebase console, expand Build and then select Firestore Database.
- Click Create database.
- Choose Standard edition and click Next.
- Don't change the Database ID, leave it set to
(default). - Select a location for your database, then click Next.
For a real app, you want to choose a location that's close to your users. - Click Start in test mode. Read the disclaimer about the security rules.
Later in this codelab, you'll add Security Rules to secure your data. Do not distribute or expose an app publicly without adding Security Rules for your database. - Click Create.
Set up Cloud Storage for Firebase
- In the left-panel of the Firebase console, expand Build and then select Storage.
- Click Get started.
- Select a location for your default Storage bucket.
Buckets inUS-WEST1,US-CENTRAL1, andUS-EAST1can take advantage of the "Always Free" tier for Google Cloud Storage. Buckets in all other locations follow Google Cloud Storage pricing and usage. - Click Start in test mode. Read the disclaimer about the security rules.
Later in this codelab, you'll add security rules to secure your data. Do not distribute or expose an app publicly without adding Security Rules for your Storage bucket. - Click Create.
Deploy Security Rules
The code already has sets of security rules for Firestore and for Cloud Storage for Firebase. After you deploy the Security Rules, the data in your database and your bucket are better protected from misuse.
- In your terminal, configure the CLI to use the Firebase project you created earlier:
When prompted for an alias, enterfirebase use --add
friendlyeats-codelab. - To deploy these Security Rules (as well as indexes that will be needed later), run this command in your terminal:
firebase deploy --only firestore,storage
- If you're asked:
"Cloud Storage for Firebase needs an IAM Role to use cross-service rules. Grant the new role?", pressEnterto select Yes.
4. Review the starter codebase
In this section, you'll review a few areas of the app's starter codebase to which you'll add functionality in this codelab.
Folder and file structure
The following table contains an overview of the folder and file structure of the app:
Folders and files | Description |
| React components for filters, headers, restaurant details, and reviews |
| Utility functions that aren't necessarily bound to React or Next.js |
| Firebase-specific code and Firebase configuration |
| Static assets in the web app, like icons |
| Routing with the Next.js App Router |
| Project dependencies with npm |
| Next.js-specific configuration (server actions are enabled) |
| JavaScript language-service configuration |
Server and client components
The app is a Next.js web app that uses the App Router. Server rendering is used throughout the app. For example, the src/app/page.js file is a server component responsible for the main page. The src/components/RestaurantListings.jsx file is a client component denoted by the "use client" directive at the beginning of the file.
Import statements
You might notice import statements like the following:
import RatingPicker from "@/src/components/RatingPicker.jsx";
The app uses the @ symbol to avoid clunky relative import paths and is made possible by path aliases.
Firebase-specific APIs
All Firebase API code is wrapped in the src/lib/firebase directory. Individual React components then import the wrapped functions from the src/lib/firebase directory, rather than importing Firebase functions directly.
Mock data
Mock restaurant and review data is contained in the src/lib/randomData.js file. Data from that file is assembled in the code in the src/lib/fakeRestaurants.js file.
5. Create an App Hosting backend
In this section, you'll set up an App Hosting backend to watch a branch on your git repository.
By the end of this section, you'll have an App Hosting backend connected to your repository in GitHub that will automatically re-build and roll out a new version of your app whenever you push a new commit to your main branch.
Create a backend
- Navigate to the App Hosting page in the Firebase console.
- Click Get started to start the backend creation flow.
- Choose a region. For a real app, you'd choose the region closest to your users.
- Follow the prompts in the Import a GitHub repository step to set up GitHub Authentication.
- From Repository, select Grant access to a new repository in GitHub and follow the prompts to enable access to the GitHub repository you created earlier.
- Click Refresh list to refresh the list, then select your repository and click Next.
- Set deployment settings:
- Set the live branch to
main. - Keep the root directory as
/. - Enable automatic rollouts.
- Set the live branch to
- Name your backend
friendlyeats-codelaband click Next. - From Associate a Firebase web app, choose Select an existing Firebase web app and select the app you added from the list
- Click Finish and deploy. You are redirected to a new page where you can see the status of your new App Hosting backend!
- Click View to view more information about your App Hosting deployment, including rollout status, logs, and usage details.
- After your rollout completes, click to open your site URL under Domains. This may take a few minutes to begin working due to DNS propagation.
- Uh oh! When you load the page, you'll see an error message that says "Application error: a server-side exception has occurred (see the server logs for more information)."
- In the Firebase console, check your App Hosting backend's Logs tab. You'll see an "Error: not implemented" log. We'll fix that in the next step when we add authentication.
You've deployed the initial web app! Every time you push a new commit to the main branch of your GitHub repository, you'll see a new build and rollout begin in the Firebase console, and your site will automatically update after the rollout completes.
6. Add authentication to the web app
In this section, you add authentication to the web app so that you can log in to it.
Add an authorized domain
Firebase Authentication will only accept sign in requests from domains that you allow. Here, we'll add your App Hosting backend's domain to the list of approved domains in your project.
- Open the App Hosting page and click View below your deployed site to access the Overview page. Copy your App Hosting backend's domain name.
- Go to the Auth Settings tab and choose the project to which you want to add an authorized domain. Then, locate the Authorized Domains section and click it.
- Click the Add domain button.
- Enter your App Hosting backend's domain.
- Click Add.
Implement the sign-in and sign-out functions
In the src/lib/firebase/auth.js file, replace the onAuthStateChanged, onIdTokenChanged, signInWithGoogle, and signOut functions with the following code:
export function onAuthStateChanged(cb) {
return _onAuthStateChanged(auth, cb);
}
export function onIdTokenChanged(cb) {
return _onIdTokenChanged(auth, cb);
}
export async function signInWithGoogle() {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
} catch (error) {
console.error("Error signing in with Google", error);
}
}
export async function signOut() {
try {
return auth.signOut();
} catch (error) {
console.error("Error signing out with Google", error);
}
}
This code uses the following Firebase APIs:
Firebase API | Description |
Adds an observer for changes to the user's sign-in state. | |
Adds an observer for changes to the user's ID token. | |
Creates a Google authentication provider instance. | |
Starts a dialog-based authentication flow. | |
Signs out the user. |
In the src/components/Header.jsx file, the code already invokes the signInWithGoogle and signOut functions.
Send authentication state to the server
In order to pass the authentication state to the server, we'll use cookies. Whenever the authentication state changes in the client, we'll update the __session cookie.
In src/components/Header.jsx, replace the useUserSession function with the following code:
function useUserSession(initialUser) {
useEffect(() => {
return onIdTokenChanged(async (user) => {
if (user) {
const idToken = await user.getIdToken();
await setCookie("__session", idToken);
} else {
await deleteCookie("__session");
}
if (initialUser?.uid === user?.uid) {
return;
}
window.location.reload();
});
}, [initialUser]);
return initialUser;
}
Read authentication state on the server
We'll use FirebaseServerApp to mirror the client's authentication state on the server.
Open src/lib/firebase/serverApp.js, and replace the getAuthenticatedAppForUser function:
export async function getAuthenticatedAppForUser() {
const authIdToken = (await cookies()).get("__session")?.value;
// Firebase Server App is a new feature in the JS SDK that allows you to
// instantiate the SDK with credentials retrieved from the client & has
// other affordances for use in server environments.
const firebaseServerApp = initializeServerApp(
// https://github.com/firebase/firebase-js-sdk/issues/8863#issuecomment-2751401913
initializeApp(),
{
authIdToken,
}
);
const auth = getAuth(firebaseServerApp);
await auth.authStateReady();
return { firebaseServerApp, currentUser: auth.currentUser };
}
Verify changes
The root layout in the src/app/layout.js file renders the header and passes in the user, if available, as a prop.
<Header initialUser={currentUser?.toJSON()} />
This means that the <Header> component renders user data, if available, during server run time. If there are any authentication updates during the page lifecycle after initial page load, the onAuthStateChanged handler handles them.
Now it's time to roll out a new build and verify what you built.
- Create a commit with the commit message "Add authentication" and push it to your GitHub repository.
git add . git commit -m "Add authentication" git push - Open the App Hosting page and, when your new rollout is complete, click the site URL to open it.
- Test authentication:
- Sign in with your Google Account and verify that your display name appears in the header after you sign in.
- Sign out and sign in again. You can repeat this step with different users.
- Optional: Right-click the web app, select View page source, and search for the display name. It appears in the raw HTML source returned from the server.
7. View restaurant information
The web app includes mock data for restaurants and reviews.
Add one or more restaurants
To insert mock restaurant data into your local Cloud Firestore database, follow these steps:
- Sign in to the web app if you haven't already. Then, select
> Add sample restaurants. Note that we don't see any restaurants show up in the Friendly Eats web app because we haven't set up data fetching code yet. We'll fix this in the next step. - In the Firebase console on the Firestore Database page, select restaurants. You see the top-level documents in the restaurant collection, each of which represents a restaurant.
- Click a few documents to explore the properties of a restaurant document.
Display the list of restaurants
Your Cloud Firestore database now has restaurants that the Next.js web app can display.
To define the data-fetching code, follow these steps:
- In the
src/app/page.jsfile, find the<Home />server component, and review the call to thegetRestaurantsfunction, which retrieves a list of restaurants at server run time. You implement thegetRestaurantsfunction in the following steps. - In the
src/lib/firebase/firestore.jsfile, replace theapplyQueryFiltersandgetRestaurantsfunctions with the following code:
function applyQueryFilters(q, { category, city, price, sort }) {
if (category) {
q = query(q, where("category", "==", category));
}
if (city) {
q = query(q, where("city", "==", city));
}
if (price) {
q = query(q, where("price", "==", price.length));
}
if (sort === "Rating" || !sort) {
q = query(q, orderBy("avgRating", "desc"));
} else if (sort === "Review") {
q = query(q, orderBy("numRatings", "desc"));
}
return q;
}
export async function getRestaurants(db = db, filters = {}) {
let q = query(collection(db, "restaurants"));
q = applyQueryFilters(q, filters);
const results = await getDocs(q);
return results.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
// Only plain objects can be passed to Client Components from Server Components
timestamp: doc.data().timestamp.toDate(),
};
});
}
- Create a commit with the commit message "Read the list of restaurants from Firestore" and push it to your GitHub repository.
git add . git commit -m "Read the list of restaurants from Firestore" git push - Open the App Hosting page in the Firebase console and wait for your new rollout to complete.
- In the web app, refresh the page. Restaurant images appear as tiles on the page.
Verify that the restaurant listings load at server run time
Using the Next.js framework, it might not be obvious when data is loaded at server run time or client-side run time.
To verify that restaurant listings load at server run time, follow these steps:
- In the web app, open DevTools and disable JavaScript.

- Refresh the web app. The restaurant listings still load. Restaurant information is returned in the server response. When JavaScript is enabled, the restaurant information is hydrated through the client-side JavaScript code.
- In DevTools, re-enable JavaScript.
Listen for restaurant updates with Cloud Firestore snapshot listeners
In the previous section, you saw how the initial set of restaurants loaded from the src/app/page.js file. The src/app/page.js file is a server component and is rendered on the server, including the Firebase data-fetching code.
The src/components/RestaurantListings.jsx file is a client component and can be configured to hydrate server-rendered markup.
To configure the src/components/RestaurantListings.jsx file to hydrate server-rendered markup, follow these steps:
- In the
src/components/RestaurantListings.jsxfile, observe the following code, which is already written for you:
useEffect(() => {
return getRestaurantsSnapshot((data) => {
setRestaurants(data);
}, filters);
}, [filters]);
This code invokes the getRestaurantsSnapshot() function, which is similar to the getRestaurants() function that you implemented in a previous step. However this snapshot function provides a callback mechanism so that the callback is invoked every time a change is made to the restaurant's collection.
- In the
src/lib/firebase/firestore.jsfile, replace thegetRestaurantsSnapshot()function with the following code:
export function getRestaurantsSnapshot(cb, filters = {}) {
if (typeof cb !== "function") {
console.log("Error: The callback parameter is not a function");
return;
}
let q = query(collection(db, "restaurants"));
q = applyQueryFilters(q, filters);
return onSnapshot(q, (querySnapshot) => {
const results = querySnapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data(),
// Only plain objects can be passed to Client Components from Server Components
timestamp: doc.data().timestamp.toDate(),
};
});
cb(results);
});
}
- In the
src/lib/firebase/firestore.jsfile, replace thegetRestaurantSnapshotById()function with the following code:
export function getRestaurantSnapshotById(restaurantId, cb) {
if (!restaurantId) {
console.log("Error: Invalid ID received: ", restaurantId);
return;
}
if (typeof cb !== "function") {
console.log("Error: The callback parameter is not a function");
return;
}
const docRef = doc(db, "restaurants", restaurantId);
return onSnapshot(docRef, (docSnap) => {
cb({
...docSnap.data(),
timestamp: docSnap.data().timestamp.toDate(),
});
});
}
Changes made through the Firestore Database page now reflect in the web app in real time.
- Create a commit with the commit message "Listen for realtime restaurant updates" and push it to your GitHub repository.
git add . git commit -m "Listen for realtime restaurant updates" git push - Open the App Hosting page in the Firebase console and wait for your new rollout to complete.
- In the web app, select
> Add sample restaurants. If your snapshot function is implemented correctly, the restaurants appear in real-time without a page refresh.
8. Save user-submitted reviews from the web app
- In the
src/lib/firebase/firestore.jsfile, replace theupdateWithRating()function with the following code:
const updateWithRating = async (
transaction,
docRef,
newRatingDocument,
review
) => {
const restaurant = await transaction.get(docRef);
const data = restaurant.data();
const newNumRatings = data?.numRatings ? data.numRatings + 1 : 1;
const newSumRating = (data?.sumRating || 0) + Number(review.rating);
const newAverage = newSumRating / newNumRatings;
transaction.update(docRef, {
numRatings: newNumRatings,
sumRating: newSumRating,
avgRating: newAverage,
});
transaction.set(newRatingDocument, {
...review,
timestamp: Timestamp.fromDate(new Date()),
});
};
This code inserts a new Firestore document representing the new review. The code also updates the existing Firestore document that represents the restaurant with updated figures for the number of ratings and the average calculated rating.
- Replace the
addReviewToRestaurant()function with the following code:
export async function addReviewToRestaurant(db, restaurantId, review) {
if (!restaurantId) {
throw new Error("No restaurant ID has been provided.");
}
if (!review) {
throw new Error("A valid review has not been provided.");
}
try {
const docRef = doc(collection(db, "restaurants"), restaurantId);
const newRatingDocument = doc(
collection(db, `restaurants/${restaurantId}/ratings`),
);
// corrected line
await runTransaction(db, (transaction) =>
updateWithRating(transaction, docRef, newRatingDocument, review),
);
} catch (error) {
console.error(
"There was an error adding the rating to the restaurant",
error,
);
throw error;
}
}
Implement a Next.js Server Action
A Next.js Server Action provides a convenient API to access form data, such as data.get("text") to get the text value from the form submission payload.
To use a Next.js Server Action to process the review form submission, follow these steps:
- In the
src/components/ReviewDialog.jsxfile, find theactionattribute in the<form>element.
<form
action={handleReviewFormSubmission}
onSubmit={() => {
handleClose();
}}
>
The action attribute value refers to a function that you implement in the next step.
- In the
src/app/actions.jsfile, replace thehandleReviewFormSubmission()function with the following code:
export async function handleReviewFormSubmission(data) {
const { firebaseServerApp } = await getAuthenticatedAppForUser();
const db = getFirestore(firebaseServerApp);
await addReviewToRestaurant(db, data.get("restaurantId"), {
text: data.get("text"),
rating: data.get("rating"),
// This came from a hidden form field.
userId: data.get("userId"),
});
}
Add reviews for a restaurant
You implemented support for review submissions, so now you can verify that your reviews are inserted into Cloud Firestore correctly.
To add a review and verify that it's inserted into Cloud Firestore, follow these steps:
- Create a commit with the commit message "Allow users to submit restaurant reviews" and push it to your GitHub repository.
git add . git commit -m "Allow users to submit restaurant reviews" git push - Open the App Hosting page in the Firebase console and wait for your new rollout to complete.
- Refresh the web app, and select a restaurant from the home page.
- On the restaurant's page, click
. - Select a star rating.
- Write a review.
- Click Submit. Your review appears at the top of the list of reviews.
- In Cloud Firestore, search the Add document panel for the document of the restaurant that you reviewed and select it.
- In the Start collection panel, select ratings.
- In the Add document panel, find the document for your review to verify that it was inserted as expected.
9. Save user-uploaded files from the web app
In this section, you add functionality so that you can replace the image associated with a restaurant when you're logged in. You upload the image to Firebase Storage, and update the image URL in the Cloud Firestore document that represents the restaurant.
To save user-uploaded files from the web app, follow these steps:
- In the
src/components/Restaurant.jsxfile, observe the code that runs when the user uploads a file:
async function handleRestaurantImage(target) {
const image = target.files ? target.files[0] : null;
if (!image) {
return;
}
const imageURL = await updateRestaurantImage(id, image);
setRestaurantDetails({ ...restaurantDetails, photo: imageURL });
}
No changes are needed to this function, but you implement the behavior of the updateRestaurantImage() function in the following steps.
- In the
src/lib/firebase/storage.jsfile, replace theupdateRestaurantImage()anduploadImage()functions with the following code:
export async function updateRestaurantImage(restaurantId, image) {
try {
if (!restaurantId) {
throw new Error("No restaurant ID has been provided.");
}
if (!image || !image.name) {
throw new Error("A valid image has not been provided.");
}
const publicImageUrl = await uploadImage(restaurantId, image);
await updateRestaurantImageReference(restaurantId, publicImageUrl);
return publicImageUrl;
} catch (error) {
console.error("Error processing request:", error);
}
}
async function uploadImage(restaurantId, image) {
const filePath = `images/${restaurantId}/${image.name}`;
const newImageRef = ref(storage, filePath);
await uploadBytesResumable(newImageRef, image);
return await getDownloadURL(newImageRef);
}
The updateRestaurantImageReference() function is already implemented for you. This function updates an existing restaurant document in Cloud Firestore with an updated image URL.
Verify the image-upload functionality
To verify that the image uploads as expected, follow these steps:
- Create a commit with the commit message "Allow users to change each restaurants' photo" and push it to your GitHub repository.
git add . git commit -m "Allow users to change each restaurants' photo" git push - Open the App Hosting page in the Firebase console and wait for your new rollout to complete.
- In the web app, verify that you're logged in and select a restaurant.
- Click
and upload an image from your filesystem. Your image leaves your local environment and is uploaded to Cloud Storage. The image appears immediately after you upload it. - Navigate to Cloud Storage for Firebase.
- Navigate to the folder that represents the restaurant. The image that you uploaded exists in the folder.

10. Summarize restaurant reviews with generative ai
In this section, you'll add a review summary feature so that a user can quickly understand what everyone thinks of a restaurant without having to read every review.
Store a Gemini API key in Cloud Secret Manager
- To use the Gemini API, you'll need an API key. Visit Google AI Studio and click Create API Key.
- Name the key whatever you like. If your project isn't listed under Choose an imported project, click Import project, check your project in the list, and then click Import. Finally, select it under Choose an imported project and click Create a key.
- App Hosting integrates with Cloud Secret Manager to allow you to store sensitive values like API keys securely:
- In a terminal, run the command to create a new secret:
firebase apphosting:secrets:set GEMINI_API_KEY- When prompted for the secret value, copy and paste your Gemini API key from Google AI Studio.
- When asked if the new secret is for production or local testing, choose "Production".
- When asked if you want to grant access so your backend's service account can access the secret, select "Yes".
- When asked if the new secret should be added to
apphosting.yaml, enterYto accept.
Your Gemini API key is now stored securely in Cloud Secret manager, and is accessible to your App Hosting backend.
Implement the review summary component
- In
src/components/Reviews/ReviewSummary.jsx, replace theGeminiSummaryfunction with the following code:export async function GeminiSummary({ restaurantId }) { const { firebaseServerApp } = await getAuthenticatedAppForUser(); const reviews = await getReviewsByRestaurantId( getFirestore(firebaseServerApp), restaurantId ); const reviewSeparator = "@"; const prompt = ` Based on the following restaurant reviews, where each review is separated by a '${reviewSeparator}' character, create a one-sentence summary of what people think of the restaurant. Here are the reviews: ${reviews.map((review) => review.text).join(reviewSeparator)} `; try { if (!process.env.GEMINI_API_KEY) { // Make sure GEMINI_API_KEY environment variable is set: // https://firebase.google.com/docs/genkit/get-started throw new Error( 'GEMINI_API_KEY not set. Set it with "firebase apphosting:secrets:set GEMINI_API_KEY"' ); } // Configure a Genkit instance. const ai = genkit({ plugins: [googleAI()], model: gemini20Flash, // set default model }); const { text } = await ai.generate(prompt); return ( <div className="restaurant__review_summary"> <p>{text}</p> <p>✨ Summarized with Gemini</p> </div> ); } catch (e) { console.error(e); return <p>Error summarizing reviews.</p>; } } - Create a commit with the commit message "Use AI to summarize reviews" and push it to your GitHub repository.
git add . git commit -m "Use AI to summarize reviews" git push - Open the App Hosting page in the Firebase console and wait for your new rollout to complete.
- Open a page for a restaurant. At the top, you should see a one-sentence summary of all the reviews on the page.
- Add a new review and refresh the page. You should see the summary change.
11. Unpublish your App Hosting site
After you complete this codelab, if you aren't going to continue to use the app, you can unpublish it to ensure that no one accesses your Firestore, Storage, and Gemini resources. You can re-publish at any time.
To unpublish an App Hosting site:
- Open App Hosting in the Firebase console.
- Locate your app's backend and click View.
- In the Backend information section, next to Domains, click Manage. This loads the Domains page.
- Next to your domain, click the More icon (three vertical dots), choose Disable domain, then click Disable to confirm.
12. Conclusion
Congratulations! You learned how to use Firebase to add features and functionality to a Next.js app. Specifically, you used the following:
- Firebase App Hosting to automatically build and deploy your Next.js code every time you push to a configured branch.
- Firebase Authentication to enable sign-in and sign-out functionality.
- Cloud Firestore for restaurant data and restaurant review data.
- Cloud Storage for Firebase for restaurant images.