Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Debug Firebase Functions Locally with the Emulator Suite

To debug Firebase Cloud Functions locally, use the Firebase Emulator Suite. Run firebase emulators:start to launch a local functions emulator, then connect your app to it using connectFunctionsEmulator. You can set breakpoints in VS Code by attaching the Node.js debugger to the emulator process, inspect logs in the Emulator UI at localhost:4000, and test both HTTP and callable functions without deploying to production.

What you'll learn

  • How to install and configure the Firebase Emulator Suite for local function testing
  • How to connect your client app to the local functions emulator
  • How to attach VS Code's Node.js debugger to set breakpoints in function code
  • How to read structured logs and debug errors in the Emulator UI
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner7 min read15-20 minFirebase CLI v13+, Cloud Functions v2 (2nd gen), Node.js 18/20/22March 2026RapidDev Engineering Team
TL;DR

To debug Firebase Cloud Functions locally, use the Firebase Emulator Suite. Run firebase emulators:start to launch a local functions emulator, then connect your app to it using connectFunctionsEmulator. You can set breakpoints in VS Code by attaching the Node.js debugger to the emulator process, inspect logs in the Emulator UI at localhost:4000, and test both HTTP and callable functions without deploying to production.

Debugging Firebase Functions Locally with the Emulator Suite

Deploying Cloud Functions to test every small change is slow and expensive. The Firebase Emulator Suite lets you run functions on your local machine with full logging, breakpoint debugging, and hot-reload support. This tutorial walks you through setting up the emulator, connecting your frontend, attaching a debugger, and reading logs to quickly find and fix issues.

Prerequisites

  • Node.js 18 or later installed
  • Firebase CLI installed globally (npm install -g firebase-tools)
  • A Firebase project initialized with Cloud Functions (firebase init functions)
  • VS Code or another editor with Node.js debugging support

Step-by-step guide

1

Install and initialize the Firebase Emulator Suite

If you have not set up emulators yet, run firebase init emulators in your project directory. Select the Functions emulator (and optionally Firestore, Auth, and Storage if your functions interact with those services). The CLI will download the required emulator binaries. Accept the default ports or customize them. The functions emulator defaults to port 5001.

typescript
1# In your Firebase project root
2firebase init emulators
3
4# Select: Functions Emulator, Firestore Emulator (optional)
5# Accept default ports or customize
6# Download emulators when prompted

Expected result: Your firebase.json now contains an emulators section with port assignments for each selected service.

2

Start the emulators

Run firebase emulators:start from your project root. The CLI compiles your functions and starts each emulator on its configured port. The Emulator UI launches at localhost:4000, giving you a dashboard to browse Firestore data, manage Auth users, and view function logs. If your functions use environment variables, create a .env file in your functions directory — the emulator reads it automatically.

typescript
1# Start all configured emulators
2firebase emulators:start
3
4# To persist data between sessions:
5firebase emulators:start --export-on-exit=./emulator-data --import=./emulator-data

Expected result: Terminal output shows each emulator starting with its port. The Emulator UI is accessible at http://localhost:4000.

3

Connect your client app to the local functions emulator

In your frontend code, use connectFunctionsEmulator to redirect all function calls to localhost instead of production. This should only run in development mode. The emulator handles both onCall and onRequest functions identically to production, including authentication context when the Auth emulator is also running.

typescript
1import { initializeApp } from 'firebase/app';
2import { getFunctions, connectFunctionsEmulator } from 'firebase/functions';
3
4const app = initializeApp(firebaseConfig);
5const functions = getFunctions(app);
6
7// Connect to local emulator in development
8if (process.env.NODE_ENV === 'development') {
9 connectFunctionsEmulator(functions, '127.0.0.1', 5001);
10}

Expected result: Client-side function calls now hit localhost:5001 instead of the production Cloud Functions endpoint.

4

Write a test function and trigger it locally

Create a simple callable function to verify the emulator is working. Export it from your functions/index.ts file. After saving, the emulator hot-reloads automatically. Call the function from your app or use curl for an onRequest function. Watch the Emulator UI Logs tab for the invocation details.

typescript
1// functions/src/index.ts
2import { onCall, HttpsError } from 'firebase-functions/v2/https';
3import { logger } from 'firebase-functions/v2';
4
5export const greet = onCall((request) => {
6 logger.info('greet called', { uid: request.auth?.uid });
7
8 if (!request.auth) {
9 throw new HttpsError('unauthenticated', 'Must be signed in');
10 }
11
12 const name = request.data.name || 'World';
13 logger.info('Greeting', { name });
14 return { message: `Hello, ${name}!` };
15});

Expected result: Calling the greet function from your app returns a greeting message, and the Emulator UI shows the log entries.

5

Attach VS Code debugger for breakpoint debugging

To set breakpoints and step through function code, start the emulator with the --inspect-functions flag. This opens a Node.js debugging port (default 9229). Then create a VS Code launch configuration that attaches to that port. Set breakpoints in your function code, trigger the function, and VS Code will pause execution at each breakpoint.

typescript
1// Start emulator with inspect flag
2// firebase emulators:start --inspect-functions
3
4// .vscode/launch.json
5{
6 "version": "0.2.0",
7 "configurations": [
8 {
9 "type": "node",
10 "request": "attach",
11 "name": "Attach to Functions Emulator",
12 "port": 9229,
13 "restart": true,
14 "skipFiles": ["<node_internals>/**"]
15 }
16 ]
17}

Expected result: VS Code connects to the emulator process. Breakpoints in function code pause execution and show local variables, call stack, and request data.

6

Read and filter logs in the Emulator UI

Open localhost:4000 in your browser and navigate to the Logs tab. All logger.info, logger.warn, and logger.error calls from your functions appear here in real time. You can filter by severity level and search for specific text. Structured log data (objects passed as second argument to logger methods) is displayed as expandable JSON, making it easy to inspect request payloads and error details.

typescript
1// Use structured logging for better debugging
2import { logger } from 'firebase-functions/v2';
3
4// Instead of:
5logger.info('User created: ' + uid);
6
7// Use structured data:
8logger.info('User created', {
9 uid,
10 email: request.data.email,
11 timestamp: new Date().toISOString()
12});

Expected result: The Emulator UI Logs tab shows real-time log entries with expandable structured data for each function invocation.

Complete working example

functions/src/index.ts
1import { onCall, onRequest, HttpsError } from 'firebase-functions/v2/https';
2import { logger } from 'firebase-functions/v2';
3import { initializeApp } from 'firebase-admin/app';
4import { getFirestore } from 'firebase-admin/firestore';
5
6initializeApp();
7const db = getFirestore();
8
9// Callable function — auto-handles auth and CORS
10export const greet = onCall((request) => {
11 logger.info('greet called', { uid: request.auth?.uid });
12
13 if (!request.auth) {
14 throw new HttpsError('unauthenticated', 'Must be signed in');
15 }
16
17 const name = request.data.name || 'World';
18 return { message: `Hello, ${name}!` };
19});
20
21// HTTP function — test with curl http://localhost:5001/PROJECT/REGION/healthCheck
22export const healthCheck = onRequest((req, res) => {
23 logger.info('Health check hit', { method: req.method });
24 res.json({ status: 'ok', timestamp: new Date().toISOString() });
25});
26
27// Function that reads Firestore — tests emulator integration
28export const getUserProfile = onCall(async (request) => {
29 if (!request.auth) {
30 throw new HttpsError('unauthenticated', 'Must be signed in');
31 }
32
33 const uid = request.auth.uid;
34 logger.info('Fetching profile', { uid });
35
36 const doc = await db.collection('users').doc(uid).get();
37
38 if (!doc.exists) {
39 logger.warn('Profile not found', { uid });
40 throw new HttpsError('not-found', 'User profile does not exist');
41 }
42
43 logger.info('Profile found', { uid, data: doc.data() });
44 return doc.data();
45});

Common mistakes when debugging Firebase Functions Locally with the Emulator Suite

Why it's a problem: Forgetting to call connectFunctionsEmulator and accidentally hitting production functions during development

How to avoid: Always wrap emulator connection calls in an environment check (process.env.NODE_ENV === 'development') and add it early in your app initialization before any function calls are made.

Why it's a problem: Functions not reloading after code changes in TypeScript projects

How to avoid: The emulator watches compiled JavaScript, not TypeScript source. Run tsc --watch in a separate terminal so TypeScript recompiles automatically on every save.

Why it's a problem: Port conflicts when starting the emulator because another process is using port 5001

How to avoid: Change the functions emulator port in firebase.json under emulators.functions.port, then update your connectFunctionsEmulator call to match the new port.

Why it's a problem: Environment variables and secrets not available in the emulator

How to avoid: Create a .env file in your functions directory with your variables. The emulator reads .env and .env.local automatically. For defineSecret values, set them as regular environment variables in .env during local development.

Best practices

  • Always use the --inspect-functions flag when you need to step through code with breakpoints
  • Use structured logging (logger.info with object payloads) instead of string concatenation for easier debugging
  • Run tsc --watch alongside the emulator for automatic TypeScript recompilation
  • Use --export-on-exit and --import flags to persist emulator data between sessions
  • Guard connectFunctionsEmulator calls with environment checks to avoid connecting to localhost in production
  • Start all dependent emulators together (Functions + Firestore + Auth) to test the full integration locally
  • Test error paths explicitly by calling functions without auth or with invalid data to verify HttpsError handling

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Show me how to set up the Firebase Emulator Suite to debug Cloud Functions locally. Include the firebase.json emulator config, how to start the emulator with inspect mode, a VS Code launch.json for attaching the debugger, and how to connect a React frontend to the local functions emulator.

Firebase Prompt

Set up Firebase Emulator Suite for local function debugging. Create a callable function with structured logging, configure the emulators in firebase.json, add a VS Code launch.json for breakpoint debugging with --inspect-functions, and show how to connect the client to the local emulator with connectFunctionsEmulator.

Frequently asked questions

Does the Firebase Functions emulator require a Blaze plan?

No. The emulator runs entirely on your local machine and does not consume any Firebase resources or require billing. You can develop and test functions on the free Spark plan.

Can I debug Firestore triggers in the emulator?

Yes. When you run both the Functions and Firestore emulators together, writing data to the Firestore emulator triggers onDocumentCreated, onDocumentUpdated, and other Firestore-triggered functions just like in production.

Why does my function return 'Function returned undefined' in the emulator?

This means your function is not returning a value or a Promise. For async functions, make sure you return the result of await calls. For callback-based code, return a Promise that resolves when the work is complete.

How do I test onRequest functions locally without a frontend?

Use curl or any HTTP client. The emulator logs the URL for each function at startup, typically http://localhost:5001/YOUR_PROJECT/us-central1/functionName. Send GET or POST requests directly to that URL.

Does the emulator support v2 (2nd gen) Cloud Functions?

Yes. The Firebase Emulator Suite fully supports both v1 and v2 Cloud Functions, including onCall, onRequest, Firestore triggers, Auth triggers, and scheduled functions.

Can I use the emulator in CI/CD pipelines for automated testing?

Yes. Run firebase emulators:exec 'npm test' to start emulators, run your test suite, and shut down automatically. This is the recommended approach for automated testing in CI environments.

Can RapidDev help set up a local Firebase development workflow?

Yes, RapidDev can configure the full Emulator Suite, set up VS Code debugging, write integration tests, and establish CI/CD pipelines that test functions locally before deploying to production.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.