For the love of God, DO NOT skip the section on setting up the Firebase Emulator . Using the Emulator effectively will save you more time and headaches than anything else.
1. Create a Firebase ProjectΒΆ
Actually, we're going to create two , separate Firebase projects:
abc-corp-dev
abc-corp-prod
abc-corp-dev will be our development environment, where we can experiment and test new features. abc-corp-prod will be our production environment for real, live users.
Create a new project from the Firebase Console . I recommend naming your projects using kebab-case, with the suffix -dev
and -prod
.
Deselect "Enable Google Analytics for this project". You can enable it later, if necessary.
I don't use Google Analytics because there are better choices for collecting data with more privacy.
Create a web app inside the project. Don't worry too much about the name - you can change it later.
Ignore the CLI and Web SDK installation instructions. We'll review them soon!
Here's a video of this process for abc-corp-prod .
In the project settings, you can label a project as a production environment.
Project Settings > General > Environment > Production
This gives it a distinctive red label, to help you recognize when you're viewing and editing the prod project.
2. Install the Firebase CLIΒΆ
Follow the instructions here to install the Firebase Command Line Interface (CLI). There are multiple ways to install the CLI. Here's the one I use π
$ npm install -g firebase-tools
After the CLI installs, run
$ firebase login
and follow the prompts to login.
How do I know if the Firebase CLI is already installed on my machine?
Run the following command in your Terminal.
$ firebase --version
If it doesn't return a version number, it's (probably) not installed.
How do I know which version of the Firebase CLI is installed on my machine?
Again, run the following command in your Terminal.
$ firebase --version
What's the current version of the Firebase CLI?
You can always check the current version on NPM . As of 2024-07-29, the current version is 13.14.2
.
How do I update my version of the Firebase CLI?
$ npm install -g firebase-tools
3. Add the Web SDK to your Next.js projectΒΆ
When you created the web app in Step 1 , Firebase gave you some instructions for adding the Web SDK. If you skipped that screen, no worries! You can find the same instructions at
Project Settings > General > Your apps > My web app > npm
Install firebase from npm.
$ npm install firebase
Run this from the working directory: abc-corp-app/
Create a file abc-corp-app/src/app/firebase.js
, then copy the SDK configuration code into it.
Either one is fine. Soon, we'll make the project dynamic so that we can easily switch between dev and prod.
Project structure firebase.js
abc-corp-app
βββ .env.development
βββ .env.production
βββ .eslintrc.json
βββ .git
β βββ ...
βββ .gitignore
βββ .vscode
β βββ settings.json
β βββ tasks.json
βββ README.md
βββ jsconfig.json
βββ next.config.mjs
βββ package-lock.json
βββ package.json
βββ postcss.config.mjs
βββ public
β βββ next.svg
β βββ vercel.svg
βββ src
β βββ app
β βββ favicon.ico
β βββ firebase.js
β βββ globals.css
β βββ layout.js
β βββ page.js
βββ tailwind.config.js
import { initializeApp } from "firebase/app"
const firebaseConfig = {
apiKey : "AIzaSyCsYcpPn8qUo0cqrsMTKy_1GBNaiqDaUUg" ,
authDomain : "scipress-dev.firebaseapp.com" ,
projectId : "scipress-dev" ,
storageBucket : "scipress-dev.appspot.com" ,
messagingSenderId : "720737082843" ,
appId : "1:720737082843:web:4586505a755f4bc6515bfe" ,
}
// Initialize Firebase
const app = initializeApp(firebaseConfig)
Replace the hard-coded values in firebase.js
with environment variables.
firebase.js
.env.development
.env.production
import { initializeApp } from "firebase/app"
const firebaseConfig = {
apiKey: process.env. NEXT_PUBLIC_FIREBASE_API_KEY ,
authDomain: process.env. NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN ,
projectId: process.env. NEXT_PUBLIC_FIREBASE_PROJECT_ID ,
storageBucket: process.env. NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET ,
messagingSenderId: process.env. NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID ,
appId: process.env. NEXT_PUBLIC_FIREBASE_APP_ID ,
}
// Initialize Firebase
const app = initializeApp (firebaseConfig)
# Development settings and API keys go here
# This file should not be tracked by git
### Firebase keys
NEXT_PUBLIC_FIREBASE_API_KEY = AIzaSyCUHzJa3Hnm6thqk5ZQG9ZP-vUWBqkPU5I
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN = abc-corp-dev-73614.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID = abc-corp-dev-73614
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET = abc-corp-dev-73614.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID = 156895892077
NEXT_PUBLIC_FIREBASE_APP_ID = 1:156895892077:web:7263700fbccb184fa02564
# Production settings and API keys go here
# This file should not be tracked by git
### Firebase keys
NEXT_PUBLIC_FIREBASE_API_KEY = AIzaSyBRf5VTuNNQWTwFsfw0Gx9mKkkf3zu-U6E
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN = abc-corp-prod-a465d.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID = abc-corp-prod-a465d
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET = abc-corp-prod-a465d.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID = 188643848192
NEXT_PUBLIC_FIREBASE_APP_ID = 1:188643848192:web:38343bb55bca1b73c9e8e5
Don't forget to add these environment variables to your Vercel project, under Settings > Environment Variables .
Development Production & Preview
4. Enable Firebase services via the consoleΒΆ
Enable the Firebase services that you want to use from the Firebase Console. You should enable the same set of services for your dev and prod projects.
For my abc-corp application, I'm going to enable Authentication, Firestore, Functions, and Storage.
This video shows me enabling services for abc-corp-dev. I've enabled the same services for abc-corp-prod (not shown).
Initialize Firebase services via firebase init
ΒΆ
After enabling services in the Firebase console, we can initialize the same services in the Next.js application using the firebase CLI command firebase init
.
$ firebase init
Run this from the working directory: abc-corp-app/
The init
command prompts you to make a number of selections. Here's what I chose π
Which Firebase features do you want to set up for this directory?
Firestore
Cloud Functions
Storage
=== Project Setup
Use an existing project
abc-corp-dev-73614 (abc-corp-dev)
=== Firestore Setup
What file should be used for Firestore Rules?
firestore.rules
What file should be used for Firestore indexes?
firestore.indexes.json
=== Functions Setup
What language would you like to use to write Cloud Functions?
JavaScript
Do you want to use ESLint to catch probable bugs and enforce style?
Yes
=== Storage Setup
What file should be used for Storage Rules?
storage.rules
Either . Later on we're going to add the other project to our config.
Setting up dev and prod environmentsΒΆ
After the init
command does it's thing, my project looks like this
Project Structure after running firebase init
abc-corp-app
βββ .env.development
βββ .env.production
βββ .eslintrc.json
βββ .firebaserc
βββ .git
β βββ ...
βββ .gitignore
βββ .vscode
β βββ settings.json
β βββ tasks.json
βββ README.md
βββ firebase.json
βββ firestore.indexes.json
βββ firestore.rules
βββ functions
β βββ .eslintrc.js
β βββ .gitignore
β βββ index.js
β βββ package-lock.json
β βββ package.json
βββ jsconfig.json
βββ next.config.mjs
βββ package-lock.json
βββ package.json
βββ postcss.config.mjs
βββ public
β βββ next.svg
β βββ vercel.svg
βββ src
β βββ app
β βββ favicon.ico
β βββ firebase.js
β βββ globals.css
β βββ layout.js
β βββ page.js
βββ storage.rules
βββ tailwind.config.js
Let's inspect the file .firebaserc
.
.firebaserc
{
"projects" : {
"default" : "abc-corp-dev-73614"
}
}
This file specifies project aliases , but at the moment it only includes my dev project. I need to add my prod project. I'll modify .firebaserc
accordingly.
.firebaserc
{
"projects" : {
"dev" : "abc-corp-dev-73614" ,
"prod" : "abc-corp-prod-a465d"
},
}
Notice I changed the name "default" to "dev".
What is this for? Eventually we're going to deploy changes in our local environment to Firebase. When we deploy a change, we need tell Firebase which project to update (dev or prod ).
Which project is currently active? To see which project is currently active, run firebase use
.
$ firebase use
> Active Project: dev ( abc-corp-dev-73614 )
>
> Project aliases for /Users/bgorman/Projects/ABC Corp/abc-corp-app:
>
> prod ( abc-corp-prod-a465d )
> * dev ( abc-corp-dev-73614 )
>
> Run firebase use --add to define a new project alias.
Run this from the working directory: abc-corp-app/
How do I change the currently active project? To change the currently active project, run firebase use < alia s >
.
$ firebase use prod
> Now using alias prod ( abc-corp-prod-a465d )
Run this from the working directory: abc-corp-app/
Test deploymentΒΆ
Now's a good time to test our updated firebase configuration. Let's make a small change to our Firestore rules.
Our Firestore rules live in three places:
In Firebase, in the dev project
In Firebase, in the prod project
In our local project, in the file firestore.rules
firestore.rules
rules_version = ' 2 ';
service cloud.firestore {
match /databases/{database }/documents {
match /{document=** } {
allow read, write : if false ;
}
}
}
Modify the local file, firestore.rules
, like this
firestore.rules
rules_version = ' 2 ';
service cloud.firestore {
match /databases/{database }/documents {
match /{document=** } {
// Add this meaningless comment!
allow read, write : if false ;
}
}
}
At this point, our prod and dev rules are out of sync with our local firestore.rules
. We need to deploy our changes.
Switch to the dev project. In other words, make the dev project the active project.
$ firebase use dev
Run this from the working directory: abc-corp-app/
Deploy the firestore rules.
$ firebase deploy --only firestore:rules
> === Deploying to 'abc-corp-dev-73614' ...
>
> i deploying firestore
> i firestore: reading indexes from firestore.indexes.json...
> i cloud.firestore: checking firestore.rules for compilation errors...
> β cloud.firestore: rules file firestore.rules compiled successfully
> i firestore: uploading rules firestore.rules...
> β firestore: released rules firestore.rules to cloud.firestore
>
> β Deploy complete !
>
> Project Console: https://console.firebase.google.com/project/abc-corp-dev-73614/overview
Run this from the working directory: abc-corp-app/
Now the firestore rules in the dev project are in sync with our local rules. But our prod project is still out-of-sync.
Switch to the prod project and deploy the rules again.
$ firebase use prod
$ firebase deploy --only firestore:rules
Run this from the working directory: abc-corp-app/
You can inspect the Firestore rules in the Firebase Console to see that your changes were properly deployed.
5. Initialize the Firebase EmulatorΒΆ
The Firebase Emulator Suite lets you to test and develop Firebase services locally. I cannot recommend it enough.
How do I set up the Emulator?
Run firebase init
.
$ firebase init
Run this from the working directory: abc-corp-app/
Choose Emulators in the subsequent prompt.
Which Firebase features do you want to set up for this directory?
β― Storage: Configure a security rules file for Cloud Storage
β―β Emulators: Set up local emulators for Firebase products
Answer the subsequent prompts. Here are my selections π
=== Emulators Setup
Which Firebase emulators do you want to set up?
β Authentication Emulator
β Functions Emulator
β Firestore Emulator
β― Database Emulator
β― Hosting Emulator
β― Pub/Sub Emulator
β Storage Emulator
Which port do you want to use for the auth emulator? 9099
Which port do you want to use for the functions emulator? 5001
Which port do you want to use for the firestore emulator? 8080
Which port do you want to use for the storage emulator? 9199
Would you like to enable the Emulator UI? Yes
Which port do you want to use for the Emulator UI (leave empty to use any available port)?
Would you like to download the emulators now? Yes
Add the following environment variables to .env.development
.env.development
### Firebase Emulator
FIRESTORE_EMULATOR_HOST="127.0.0.1:8080"
FIREBASE_AUTH_EMULATOR_HOST="127.0.0.1:9099"
FIREBASE_STORAGE_EMULATOR_HOST="127.0.0.1:9199"
Start the emulator.
$ firebase emulators:start
> i emulators: Starting emulators: auth, functions, firestore, storage
> β functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: database, hosting, pubsub, dataconnect
> i firestore: Firestore Emulator logging to firestore-debug.log
> β firestore: Firestore Emulator UI websocket is running on 9150.
> i ui: Emulator UI logging to ui-debug.log
> i functions: Watching "/Users/spngebob/Projects/ABC Corp/abc-corp-app/functions" for Cloud Functions...
> β functions: Your requested "node" version "18" doesn 't match your global version "20". Using node@20 from host.
> Serving at port 8071
>
> β functions: Loaded functions definitions from source: .
>
> βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
> β β All emulators ready! It is now safe to connect your app. β
> β i View Emulator UI at http://127.0.0.1:4000/ β
> βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
>
> ββββββββββββββββββ¬βββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
> β Emulator β Host:Port β View in Emulator UI β
> ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββββββββββββββββββ€
> β Authentication β 127.0.0.1:9099 β http://127.0.0.1:4000/auth β
> ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββββββββββββββββββ€
> β Functions β 127.0.0.1:5001 β http://127.0.0.1:4000/functions β
> ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββββββββββββββββββ€
> β Firestore β 127.0.0.1:8080 β http://127.0.0.1:4000/firestore β
> ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββββββββββββββββββ€
> β Storage β 127.0.0.1:9199 β http://127.0.0.1:4000/storage β
> ββββββββββββββββββ΄βββββββββββββββββ΄ββββββββββββββββββββββββββββββββββ
> Emulator Hub running at 127.0.0.1:4400
> Other reserved ports: 4500, 9150
Click the Emulator UI link to open the Emulator in your browser.
Verbose console loggingΒΆ
I prefer to launch the emulator using the --debug
flag, as it logs more information to the console. This is particularly helpful when debugging.
$ firebase emulators:start --debug
Furthermore, I recommend setting this up as a task , and then creating a Task Button for it.
.vscode/tasks.json
.vscode/settings.json
{
"version" : "2.0.0" ,
"tasks" : [
{
"label" : "Firebase emulators: debug" ,
"type" : "shell" ,
"command" : "firebase emulators:start --debug" ,
"presentation" : {
"panel" : "dedicated"
}
},
{
"label" : "Next.js: run" ,
"type" : "npm" ,
"script" : "dev" ,
"presentation" : {
"panel" : "dedicated"
}
},
]
}
{
"VsCodeTaskButtons.showCounter" : false ,
"VsCodeTaskButtons.tasks" : [
{
"label" : "πΊ Next" ,
"task" : "Next.js: run"
},
{
"label" : "π₯ Firebase" ,
"task" : "Firebase emulators: debug"
},
]
}
6. Instantiate Firebase Services using the Web SDKΒΆ
Update the file src/app/firebase.js
to instantiate and export an instance for each firebase service that you want to use client-side.
src/app/firebase.js
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app"
import { connectAuthEmulator, getAuth } from "firebase/auth"
import { connectFirestoreEmulator, getFirestore } from "firebase/firestore"
import { connectFunctionsEmulator, getFunctions } from "firebase/functions"
import { connectStorageEmulator, getStorage } from "firebase/storage"
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: process.env. NEXT_PUBLIC_FIREBASE_API_KEY ,
authDomain: process.env. NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN ,
projectId: process.env. NEXT_PUBLIC_FIREBASE_PROJECT_ID ,
storageBucket: process.env. NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET ,
messagingSenderId: process.env. NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID ,
appId: process.env. NEXT_PUBLIC_FIREBASE_APP_ID ,
}
// Initialize Firebase
const app = initializeApp (firebaseConfig)
// Instantiate services
export const auth = getAuth (app)
export const db = getFirestore (app)
export const functions = getFunctions (app)
export const storage = getStorage (app)
// Emulator
if (process.env. NODE_ENV == "development" ) {
connectAuthEmulator (auth, "http://127.0.0.1:9099" )
connectFunctionsEmulator (functions, "127.0.0.1" , 5001 )
connectFirestoreEmulator (db, "127.0.0.1" , 8080 )
connectStorageEmulator (storage, "127.0.0.1" , 9199 )
}
Now you can use these objects to write client-side code. For example, if you want to create a password based account, you can follow the Firebase docs here , where they suggest doing
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth" ;
const auth = getAuth ();
createUserWithEmailAndPassword (auth, email, password)
. then (( userCredential ) => {
// Signed up
const user = userCredential.user;
// ...
})
. catch (( error ) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
BUT, instead of re-instantiating the auth
object, you can just import it from firebase.js
.
import { auth } from "@/app/firebase" ;
import { createUserWithEmailAndPassword } from "firebase/auth" ;
createUserWithEmailAndPassword (auth, email, password)
. then (( userCredential ) => {
// Signed up
const user = userCredential.user;
// ...
})
. catch (( error ) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
7. Instantiate Firebase Services using the Admin SDKΒΆ
If you're using Next.js, you may want to do some things server-side as opposed to client-side . For example, you may want to fetch data from Firestore or Storage inside a page.jsx
file. In such cases, you'll want to use the Firebase Admin SDK - not the Web SDK .
To do this, we're going to create a file called src/app/firebaseAdmin.js
, very similar to src/app/firebase.js
.
Create and download a service account key from the Firebase Console.
Project Settings > Service accounts > Generate new private key
Do this for both the dev and prod projects.
Create the following environment variables inside .env.development
.env.development
# Development settings and API keys go here
# This file should not be tracked by git
### Firebase keys
NEXT_PUBLIC_FIREBASE_API_KEY=AIzaSyCUHG9ZP-vUWBqkPU5I
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=abc-corp-dev-73614.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=abc-corp-dev-73614
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=abc-corp-dev-73614.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=156895892077
NEXT_PUBLIC_FIREBASE_APP_ID=1:1569892077:web:7263700fbcb18fa02564
### Firebase private data
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-nuw3j@abc-corp-dev-73614.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY='"-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDf7+nbNAQPIa7D\nBWoL0yYllr4lZviQK5j8ReM8UH61MV393YsLasSCcJN25K4995AIjt0eTmHrJk1A\n6YuBNQhidL2icRK2ors84z1pzfCO5DKZxKrK4f450SFcsHSxkNh0Zvmn1zqke\ndRdW5/Zee1FS8cp5gIEXga1C+R0/O0q/CFsuTtPv7BB/WkyBcgpZfdch77U5Fy0x\nFPK3HvlSFwRbMtLZtVlQZ2XIJqlZcmC07ypXJv1OkssbRwI48WWqkAy8fNuEMa\nibaLh5Ry/ODo0OIuqSvnYzgKArs+w8OPi5U9gYjY90+beskWwJpwH4dmDACMqO\nJdnzKBv1AgMBAAECggEAUCO+F43CZB3kOnOuE0RBsxMCwkTa8FFwopu5R8++AK\naIcHsxLG931N++8XsuhvmFhUp8vVgMLo5Ft/wgH4fys3buwQB0rGIQQNSL4b8l\noqTgkFGXFt7SJvqLaC+6irRY7rUXm3mVSUqTCy1hHEfafyn7kUbdK+ni9QRMpKsq\nmcX+6iaX16xmZC5KW7pPP/fy/73fxvCJ2lHG5U53//BDZ3KyXnw4nTNJBaKIjB\nIb1lY0HHb6QTBM21bjLmWNTpUCEtCNWWD6oHeMcDv2Q3BlVhCPhO8mqAAfD5f2\nl9NwFeH7qpj8HI/89GzCuSep/JbkSgXESce1ipmXAwKBgQDxpjbslFOMhzBk5hVr\nwbY6oE4tB2ID6Y0FimSNI/EscNC5njcSM2QLhYdsmWPa+ocxFezhZdKxecPv7qpd\nyUn08bNn67i83eeI23y+pdidAcCaT+wewy8QEbYsXpTB4D33Zxyn8Ypg3FiRY4pu\nrjL3fWukpVr3iYxgpW7sOMxtYwKBgQDtPGwczWn4ynl9C/A+XUK5yu1krlGENZis\nAixIMLlrKzcgBO0AfdUOk99FqrJ4FlVeD1p9SPGjDj5obIdmFYBp4QLLCKiQISwx\nVYH6XFZjqu8FDF4SGN3GTv3DOqq7pC7r13a91UhuVqGO8JWTVSiw1VwPPWixKVJW\nbin85CrcxwKBgQCbnnwOfTMU89bBk77Fx1kwhh3yN6fydwx/PfGlRzlAcmKotvAm\nQ3wxQ0vEL0A0PFSl0sXbHfTCoHUDZgLHJSMAoEr2KMKutoUuCJNcP3exHPjJJ6Ea\n6n27bnyQNFS+Skf9tKSqrw17dUuATcdPodVmtpmEzYw4j5ccgk1dyPI1mwKBgQDE\ngyuPBDzHAHl4Mr5z+m6+VjElGBygJoFr4nTp66p78pU8hkktc8txLYs5gYT2+x\ndYhghV7b0X2ypYLPFtQx08L9TpZwEjcJA12Rq9NIXEFEPmLe1SnxYRRyUIMp\nixZqE1VnSM2VCQbtvznwmRwj55tkPTVeMO0I9bvnmwKBgFK3m9EgCj0aK2HLJF3J\nTNtVnbzt/PU3g0JaM6BN1LaNZhj8tPRoX31RR7wpmzOrZ4vK0Q97mboJwocnW2IH\nZXarYEJ/LHwDNgLevcMetDQMaBvU/w56HwhuXu3rI6RYD7vdS9aq+R6PKRM+bi\nEHYLGE48LbjaaQSuPQgOF4\n-----END PRIVATE KEY-----\n"'
The values for FIREBASE_CLIENT_EMAIL
and FIREBASE_PRIVATE_KEY
can be obtained from inside the .json
key file you downloaded in Step 1.
Locally, you need to wrap FIREBASE_PRIVATE_KEY
in single quotes '
since its value starts and ends with double quotes "
. Exclude the single quotes when you add the value to Vercel.
Do not preface these environment variables with NEXT_PUBLIC_
! These variables should remain hidden from the client (i.e. they should only be available server-side).
Repeat this process for the prod environment and .env.production
file.
Create a new file called src/app/firebaseAdmin.js
with the following contents.
src/app/firebaseAdmin.js
import { cert, getApp, getApps, initializeApp } from "firebase-admin/app"
import { getAuth } from "firebase-admin/auth"
import { getFirestore } from "firebase-admin/firestore"
const adminApp =
getApps (). length > 0
? getApp ()
: process.env. NODE_ENV == "development"
? initializeApp ({ projectId: process.env. NEXT_PUBLIC_FIREBASE_PROJECT_ID })
: initializeApp ({
credential: cert ({
projectId: process.env. NEXT_PUBLIC_FIREBASE_PROJECT_ID ,
clientEmail: process.env. FIREBASE_CLIENT_EMAIL ,
privateKey: JSON . parse (process.env. FIREBASE_PRIVATE_KEY ),
}),
})
export const adminAuth = getAuth (adminApp)
export const adminDB = getFirestore (adminApp)
This file instantiates and exports an adminAuth
instance and an adminDB
instance.
Now you can import these objects into a page.jsx
file to implement server-side logic. For example, maybe you want to fetch data from Firestore before rendering a certain page..
src/app/posts/[postId]/page.jsx
import { adminDB } from "@/app/firebaseAdmin"
export default async function Page ({ params }) {
// Get the requested postId from the route params
const { postId } = params
// Fetch the requested post
const postRef = adminDB. collection ( 'posts' ). doc (postId);
const postDoc = await postRef. get ();
const post = postDoc. data ();
// Render the page
return < div > { post.title } </ div >
}
8. Configure Firebase FunctionsΒΆ
Firebase functions live in the functions/
directory.
Project Structure
abc-corp-app
βββ functions
β βββ .eslintrc.js
β βββ .gitignore
β βββ index.js
β βββ package-lock.json
β βββ package.json
...
Initially, you can define functions inside index.js
, but this can become unwieldy as your project grows. My preference is to create a single .js
for each function inside the functions/
directory. Then I import and export each function from index.js
.
For example, I might add a createPost()
function as follows
Project Structure functions/createPost.js
functions/index.js
functions/package.json
abc-corp-app
βββ functions
β βββ .eslintrc.js
β βββ .gitignore
β βββ createPost.js <-- add this file
β βββ index.js
β βββ package-lock.json
β βββ package.json
...
import { onCall } from "firebase-functions/v2/https"
export const createPost = onCall (( request ) => {
// Create a post
console. log ( "Creating a new post..." )
// ...
})
import { createPost as _createPost } from "./createPost.js"
export const createPost = _createPost
{
"name" : "functions" ,
"description" : "Cloud Functions for Firebase" ,
"type" : "module" , <-- add this!
"scripts" : {
"lint" : "eslint ." ,
"serve" : "firebase emulators:start --only functions" ,
"shell" : "firebase functions:shell" ,
"start" : "npm run shell" ,
"deploy" : "firebase deploy --only functions" ,
"logs" : "firebase functions:log"
},
"engines" : {
"node" : "18"
},
"main" : "index.js" ,
"dependencies" : {
"firebase-admin" : "^12.1.0" ,
"firebase-functions" : "^5.0.0"
},
"devDependencies" : {
"eslint" : "^8.15.0" ,
"eslint-config-google" : "^0.14.0" ,
"firebase-functions-test" : "^3.1.0"
},
"private" : true
}
Notice I added "type": "module"
to package.json
to enable ES 6 modules (import
as opposed to require
statements).
Deploying Firebase FunctionsΒΆ
You can deploy your locally-defined firebase functions using the firebase CLI.
How do I deploy all my functions?
dev prod
$ firebase use dev
$ firebase deploy --only functions
$ firebase use prod
$ firebase deploy --only functions
How do I deploy a specific function?
dev prod
$ firebase use dev
$ firebase deploy --only functions:createPost
$ firebase use prod
$ firebase deploy --only functions:createPost
How do I deploy two or more specific functions? Delineate the function names with spaces.
dev prod
$ firebase use dev
$ firebase deploy --only functions:createPost editPost deletePost
$ firebase use prod
$ firebase deploy --only functions:createPost editPost deletePost
How do I delete a firebase function?
dev prod
$ firebase use dev
$ firebase functions:delete createPost
$ firebase use prod
$ firebase functions:delete createPost
This doesn't delete your function code. It just deletes the function from Firebase. You can also delete a function manually, from the Firebase Console.
Use the --debug
flag if something goes wrong. For instance,
firebase deploy --only functions --debug
Cross-Origin Resource Sharing (CORS)ΒΆ
Inevitably, you'll need to deal with CORS at some point.
Create a file functions/main.js
with the following contents.
Project Structure functions/main.js
abc-corp-app
βββ functions
β βββ .eslintrc.js
β βββ .gitignore
β βββ createPost.js
β βββ index.js
β βββ main.js <-- add this file
β βββ package-lock.json
β βββ package.json
...
import { initializeApp } from "firebase-admin/app"
// Initialize app
initializeApp ()
setGlobalOptions ({ region: "your-region" })
// CORS
export const cors = [
"http://localhost:3000" ,
"https://www.yourdomain.com" ,
]
You'll need to modify this to suit your specific region and domain name.
Update your functions to use the cors
array exported from main.js
.
functions/createPost.js
import { onCall } from "firebase-functions/v2/https"
import { cors } from "./main.js"
export const createPost = onCall ({ cors }, ( request ) => {
// Create a post
console. log ( "Creating a new post..." )
// ...
})
Environment VariablesΒΆ
Firebase functions support the use of environment variables .
Create a file functions/.env.dev
Create a file functions/.env.prod
Define any env vars you want to use, in these files. For example,
functions/.env.dev
functions/.env.prod
SITE_URL=https://staging.mydomain.com/
SITE_URL=https://www.mydomain.com/
Now you can reference these env vars inside your Firebase functions using process.env.VARNAME
.
Furthermore, when you deploy your functions to dev
/prod
, the corresponding environment variables will be used in the deployment.
SecretsΒΆ
If you want to use a top secret value like an API key, you can use a secret .
DebuggingΒΆ
To debug a Firebase function with VS Code,
Create a file .vscode/launch.json
with the following contents.
.vscode/launch.json
{
"version" : "0.2.0" ,
"configurations" : [
{
"name" : "Firebase: debug funcs" ,
"type" : "node" ,
"request" : "attach" ,
"port" : 9229 ,
"restart" : true ,
"skipFiles" : [ "<node_internals>/**" ]
},
]
}
Add the following task to .vscode/tasks.json
.
.vscode/tasks.json
{
"version" : "2.0.0" ,
"tasks" : [
{
"label" : "Firebase emulators: debug" ,
"type" : "shell" ,
"command" : "firebase emulators:start --inspect-functions --debug" ,
"presentation" : {
"panel" : "dedicated"
}
},
...
]
}
Optionally, add a task button for this in .vscode/settings.json
.
.vscode/settings.json
{
"VsCodeTaskButtons.showCounter" : false ,
"VsCodeTaskButtons.tasks" : [
{
"label" : "π₯ Firebase Debug" ,
"task" : "Firebase emulators: debug"
},
...
]
}
Run the firebase emulator with the --inspect-functions
flag
Choose Firebase: debug funcs from the Run and Debug window in VS Code.
Click Start Debugging
Set a breakpoint somewhere in the function you want to debug
Invoke the function