Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions skills/firebase-functions-basics/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: firebase-functions-basics
description: Guide for setting up and using Cloud Functions for Firebase. Use this skill when the user's app requires server-side logic, integrating with third-party APIs, or responding to Firebase events.
compatibility: This skill requires the Firebase CLI. Install it by running `npm install -g firebase-tools`.
---

## Prerequisites

- **Firebase Project**: Created via `firebase projects:create` (see `firebase-basics`).
- **Firebase CLI**: Installed and logged in (see `firebase-basics`).

## Core Concepts

Cloud Functions for Firebase lets you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your code is stored in Google's cloud and runs in a managed environment.

### 1st-gen vs 2nd-gen

This section **only applies to Node.js**, since all Python functions are 2nd gen.

- Always use 2nd-gen functions for new development. They are powered by Cloud Run and offer better performance and configurability.
- Use `firebase-functions` SDK version 7.0.0 and above.
- Use 2nd gen Auth triggers if they are available. If not, fallback to 1st gen for Auth triggers only.
- Avoid writing functions triggered by Analytics events. These are not supported in 2nd gen and are discouraged.
- Use top-level imports (e.g., `firebase-functions/https`). These are 2nd gen by default. If 1st gen is required, import from the `firebase-functions/v1` import path.

### Secrets Management

For sensitive information like API keys (e.g., for LLMs, payment providers, etc.), **always** use `defineSecret` (Node.js) or `SecretParam` (Python). This stores the value securely in Cloud Secret Manager.

### Firebase Admin SDK

To interact with Firebase services like Firestore, Auth, or RTDB from within your functions, you need to initialize the Firebase Admin SDK. Call `initializeApp` without any arguments so that Application Default Credentials are used.

## Workflow

### 1. Provisioning & Setup

Functions can be initialized using the CLI or manually. Ensure you have initialized the Firebase Admin SDK to interact with other Firebase services.

See the language-specific references to learn how to properly install and initialize the Admin SDK:
- Node.js: [references/node_setup.md](references/node_setup.md)
- Python: [references/python_setup.md](references/python_setup.md)

### 2. Writing Functions

For Node.js, see [references/node_setup.md](references/node_setup.md). For Python, see [references/python_setup.md](references/python_setup.md)

### 3. Local Development & Deployment

The CLI will prompt for a secret's value at deploy time. Alternatively, a human can set the secret using the Firebase CLI command:

```bash
firebase functions:secrets:set <SECRET_NAME>
```

#### Development Commands

See the language references for detailed setup and dependency instructions.

```bash
# Run emulators for local development.
firebase emulators:start

# If you need to run another script with the emulator, like tsc,
# use `emulators:exec` instead of `emulators:start`.
firebase emulators:exec "tsc --watch"

# Deploy functions (Building is handled automatically if needed)
firebase deploy --only functions
```
78 changes: 78 additions & 0 deletions skills/firebase-functions-basics/references/node_setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Cloud Functions for Firebase setup guide (Node.js)

This guide provides a step-by-step process for setting up Cloud Functions with the Node.js runtime, tailored for coding agents.

## 1. Install dependencies

Run the init command for functions:

```bash
firebase init functions
```

This is an **interactive** CLI command. When asked which language to use, choose **TypeScript**. When asked if you'd like to install dependencies, choose **yes**.

## 2. Create a 2nd-gen HTTP function

Replace the contents of `src/index.ts` (or `index.js`) with the following code to create a simple, modern v2 HTTP endpoint along with a Firestore-triggered function.

```typescript
import { setGlobalOptions, onInit } from "firebase-functions";
import { onRequest } from "firebase-functions/https";
import { onDocumentCreated } from "firebase-functions/firestore";
import * as logger from "firebase-functions/logger";
import { defineString, defineInt } from "firebase-functions/params";

import { initializeApp } from "firebase-admin/app";

// Configurable parameters
const scaleLimit = defineInt("MAX_INSTANCES", { default: 1 });
const greeting = defineString("GREETING", { default: "Hello" });

onInit(() => {
initializeApp();
setGlobalOptions({ maxInstances: scaleLimit });
});

export const helloWorld = onRequest(async (request, response) => {
logger.info("Request received!", request);
response.send(`${greeting.value()} from Firebase!`);
});

export const newDoc = onDocumentCreated(
"/words/{wordId}",
async (event) => {
logger.info("New word!", event.data?.data());
}
);
Comment on lines +20 to +47
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The provided code snippet for a 2nd-gen function is incorrect as it mixes APIs from both v1 and v2 of Cloud Functions, which will lead to errors. Specifically:

  • Imports for onRequest and onDocumentCreated should be from firebase-functions/v2/... subpaths for 2nd-gen functions.
  • The onInit lifecycle hook is for v1 functions and should not be used.
  • setGlobalOptions should be imported from firebase-functions/v2 and called at the top level.
  • Logging the entire request object can be a security risk and produce noisy logs. It's better to log specific, non-sensitive properties.

Here is a corrected version of the snippet that properly implements 2nd-gen functions:

import { onRequest } from "firebase-functions/v2/https";
import { onDocumentCreated } from "firebase-functions/v2/firestore";
import * as logger from "firebase-functions/logger";
import { defineString, defineInt } from "firebase-functions/params";
import { initializeApp } from "firebase-admin/app";
import { setGlobalOptions } from "firebase-functions/v2";

initializeApp();

// Configurable parameters
const scaleLimit = defineInt("MAX_INSTANCES", { default: 1 });
const greeting = defineString("GREETING", { default: "Hello" });

setGlobalOptions({ maxInstances: scaleLimit });

export const helloWorld = onRequest(async (request, response) => {
  logger.info("Request received!", { body: request.body });
  response.send(`${greeting.value()} from Firebase!`);
});

export const newDoc = onDocumentCreated(
  "/words/{wordId}",
  async (event) => {
    logger.info("New word!", event.data?.data());
  }
);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is your source for that information? The default import has been 2nd gen for a while now, so I want to correct the source you're using.

```

**Key points for the agent:**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Key points for the agent:**
**Key points:**

Its all for the agent

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Key points for the agent:**
**Key points:**

Its all for the agent


- Use `logger` for structured logging.
- Use `defineString`, `defineInt`, `defineSecret` for environment configuration.
- Recommend a `maxInstances` setting of 1 for all functions until correct behavior can be verified.

## 3. Local Development and Testing

Use the Firebase Emulators to test your function locally before deploying.

This command starts the emulators, and starts the TypeScript compiler in watch mode:

```bash
# Start the functions emulator
firebase emulators:exec "tsc --watch"
```

You can then interact with the function at the local URL provided by the emulator.

## 4. Deploy to Firebase

Once testing is complete, deploy the function to your Firebase project.

```bash
# Deploy only the functions
firebase deploy --only functions
```

The agent will be prompted to set any parameters defined with `defineString` or other `define` functions that do not have a default value.
87 changes: 87 additions & 0 deletions skills/firebase-functions-basics/references/python_setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Cloud Functions for Firebase setup guide (Python)

This guide provides a step-by-step process for setting up Cloud Functions with the Python runtime, tailored for coding agents.

## 1. Create a 2nd-gen HTTP function

Replace the contents of `functions/main.py` with the following code to create a simple, modern v2 HTTP endpoint along with a Firestore-triggered function.

```python
from firebase_functions import https_fn, firestore_fn, options, params, init
from firebase_admin import initialize_app, firestore
import google.cloud.firestore

# Configurable parameters
SCALE_LIMIT = params.IntParam("MAX_INSTANCES", default=1)
GREETING = params.StringParam("GREETING", default="Hello")

admin_app = None

@init
def initialize():
options.set_global_options(max_instances=SCALE_LIMIT)

global admin_app
admin_app = initialize_app()
Comment on lines +18 to +25
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are a couple of issues in the initialize function:

  1. The admin_app global variable is assigned but never used, which is confusing. Calling initialize_app() is sufficient as it sets up the default app instance.
  2. The SCALE_LIMIT parameter is an IntParam object. To use its integer value for max_instances, you must call the .value() method. Passing the object directly will cause a runtime error.
Suggested change
admin_app = None
@init
def initialize():
options.set_global_options(max_instances=SCALE_LIMIT)
global admin_app
admin_app = initialize_app()
@init
def initialize():
options.set_global_options(max_instances=SCALE_LIMIT.value())
initialize_app()


@https_fn.on_request(
cors=options.CorsOptions(cors_origins="*", cors_methods=["get", "post"])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a wildcard * for cors_origins allows any origin to access your function, which can be a security risk. For production environments, it is strongly recommended to restrict this to a specific list of allowed domains. For a guide, it's better to show a more secure example or add a note about this.

Suggested change
cors=options.CorsOptions(cors_origins="*", cors_methods=["get", "post"])
cors=options.CorsOptions(cors_origins=["https://your-web-app.com"], cors_methods=["get", "post"])

)
def helloworld(req: https_fn.Request) -> https_fn.Response:
"""A simple HTTP-triggered function."""
print("Request received!")
return https_fn.Response(f"{GREETING.value()} from Firebase!")


@firestore_fn.on_document_created(document="words/{wordId}")
def newdoc(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:
"""Triggered when a new document is created in /words."""
if event.data is None:
return
print(f"New word: {event.data.to_dict()}")
```

**Key points for the agent:**

- Use `print()` for logging (output goes to Cloud Logging automatically).
- Use `params.StringParam`, `params.IntParam`, and `params.SecretParam` for environment configuration.
- Recommend a `max_instances` setting of 1 for all functions until correct behavior can be verified.
- The entry point is always `functions/main.py`. All functions must be defined in or imported into this file.
- Dependencies go in `functions/requirements.txt`.

## 2. Install dependencies

Ensure `functions/requirements.txt` lists the needed packages:

```
firebase-functions
firebase-admin
```

Then install with:

```bash
pip install -r functions/requirements.txt
```

## 3. Local Development and Testing

Use the Firebase Emulators to test your function locally before deploying.

A human should run the following command in a separate terminal window to start the emulators:

```bash
# Start the functions emulator
firebase emulators:start
```

A human can then interact with the function at the local URL provided by the emulator.

## 4. Deploy to Firebase

Once testing is complete, deploy the function to your Firebase project.

```bash
# Deploy only the functions
firebase deploy --only functions
```