-
Notifications
You must be signed in to change notification settings - Fork 216
Add dotguides #1736
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add dotguides #1736
Changes from 12 commits
94fdcef
90899a8
6757a45
07d3019
b12dd46
89d5c50
f8fb34e
f46a92c
d4ee0c9
1bbf723
079ad3f
0ac2a60
ac93f7d
3eaa99d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"description": "Use this library to build serverless functions for event triggers and HTTP using Cloud Functions for Firebase", | ||
"mcpServers": { | ||
"firebase": { | ||
"command": "npx", | ||
"args": ["-y", "firebase-tools@latest", "experimental:mcp"] | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# Cloud Functions for Firebase setup guide | ||
|
||
This guide provides a step-by-step process for setting up Cloud Functions, tailored for coding agents. | ||
|
||
## 1. 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. | ||
|
||
```typescript | ||
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"; | ||
|
||
// Configurable parameters | ||
const scaleLimit = defineInt("MAX_INSTANCES", { default: 1 }); | ||
const greeting = defineString("GREETING", { default: "Hello" }); | ||
|
||
export const helloWorld = onRequest({ maxInstances: scaleLimit }, async (request, response) => { | ||
logger.info("Request received!", request); | ||
response.send(`${greeting.value()} from Firebase!`); | ||
}); | ||
|
||
export const newDoc = onDocumentCreated( | ||
{ maxInstances: scaleLimit }, | ||
"/words/{wordId}", | ||
async (event) => { | ||
logger.info("New word!", event.data.data()); | ||
} | ||
); | ||
``` | ||
taeold marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
**Key points for the agent:** | ||
|
||
- Use `logger` for structured logging. | ||
- Use `defineString`, `defineInt`, `defineSecret` for environment configuration instead of `functions.config()`. | ||
- Recommend a `maxInstances` setting of 1 for all functions until correct behavior can be verified. | ||
|
||
## 2. Build TypeScript | ||
|
||
Compile your TypeScript code to JavaScript. | ||
|
||
```bash | ||
npm run build | ||
``` | ||
|
||
## 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 --only functions | ||
``` | ||
|
||
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 | ||
``` | ||
|
||
The agent will be prompted to set any parameters defined with `defineString` or other `define` functions that do not have a default value. |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this can be really powerful, but we need to be careful. Maybe the goal of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that does sound safer. I've changed step 1 to find and prompt for a single function to migrate. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# Upgrading a 1st gen to 2nd gen | ||
|
||
This guide provides a step-by-step process for migrating a single Cloud Function from 1st to 2nd generation. Migrate functions one-by-one. Run both generations side-by-side before deleting the 1st gen function. | ||
|
||
## 1. Identify a 1st-gen function to upgrade | ||
|
||
Find all 1st-gen functions in the directory. 1st-gen functions used a namespaced API like this: | ||
|
||
**Before (1st Gen):** | ||
|
||
```typescript | ||
import * as functions from "firebase-functions"; | ||
|
||
export const webhook = functions.https.onRequest((request, response) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
Sometimes, they'll explicitly import from the `firebase-functions/v1` subpackage, but not always. | ||
Comment on lines
+7
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @taeold this seems like a cool opportunity for an mcp tool There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MCP tools for something very niche like "find all v1 functions" are a bit of a tough sell for me, but what you can do is pre-specify shell commands that will get the info you want. For example a |
||
|
||
Ask the human to pick a **single** function to upgrade from the list of 1st gen functions you found. | ||
|
||
## 2. Update Dependencies | ||
|
||
Ensure your `firebase-functions` and `firebase-admin` SDKs are up-to-date, and you are using a recent version of the Firebase CLI. | ||
|
||
## 3. Modify Imports | ||
|
||
Update your import statements to use the top-level modules. | ||
|
||
**After (2nd Gen):** | ||
|
||
```typescript | ||
import { onRequest } from "firebase-functions/https"; | ||
``` | ||
|
||
## 4. Update Trigger Definition | ||
|
||
The SDK is now more modular. Update your trigger definition accordingly. | ||
|
||
**After (2nd Gen):** | ||
|
||
```typescript | ||
export const webhook = onRequest((request, response) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
Here are other examples of trigger changes: | ||
|
||
### Callable Triggers | ||
|
||
**Before (1st Gen):** | ||
|
||
```typescript | ||
export const getprofile = functions.https.onCall((data, context) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
**After (2nd Gen):** | ||
|
||
```typescript | ||
import { onCall } from "firebase-functions/https"; | ||
|
||
export const getprofile = onCall((request) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
### Background Triggers (Pub/Sub) | ||
|
||
**Before (1st Gen):** | ||
|
||
```typescript | ||
export const hellopubsub = functions.pubsub.topic("topic-name").onPublish((message) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
**After (2nd Gen):** | ||
|
||
```typescript | ||
import { onMessagePublished } from "firebase-functions/pubsub"; | ||
|
||
export const hellopubsub = onMessagePublished("topic-name", (event) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
## 5. Use Parameterized Configuration | ||
|
||
Migrate from `functions.config()` to the new `params` module for environment configuration. | ||
|
||
**Before (`.runtimeconfig.json`):** | ||
|
||
```json | ||
{ | ||
"someservice": { | ||
"key": "somesecret" | ||
} | ||
} | ||
``` | ||
|
||
**And in code (1st Gen):** | ||
|
||
```typescript | ||
const SKEY = functions.config().someservice.key; | ||
``` | ||
|
||
**After (2nd Gen):** | ||
Define params in your code and set their values during deployment. | ||
|
||
**In `index.ts`:** | ||
|
||
```typescript | ||
import { defineString } from "firebase-functions/params"; | ||
|
||
const SOMESERVICE_KEY = defineString("SOMESERVICE_KEY"); | ||
``` | ||
|
||
Use `SOMESERVICE_KEY.value()` to access the value. For secrets like API keys, use `defineSecret`. | ||
|
||
**In `index.ts`:** | ||
|
||
```typescript | ||
import { defineSecret } from "firebase-functions/params"; | ||
|
||
const SOMESERVICE_KEY = defineSecret("SOMESERVICE_KEY"); | ||
``` | ||
|
||
The human will be prompted to set the value on deployment. The value will be stored securely in Cloud Secret Manager. | ||
|
||
## 6. Update Runtime Options | ||
|
||
Runtime options are now set directly within the function definition. | ||
|
||
**Before (1st Gen):** | ||
|
||
```typescript | ||
export const func = functions | ||
.runWith({ | ||
// Keep 5 instances warm | ||
minInstances: 5, | ||
}) | ||
.https.onRequest((request, response) => { | ||
// ... | ||
}); | ||
``` | ||
|
||
**After (2nd Gen):** | ||
|
||
```typescript | ||
import { onRequest } from "firebase-functions/https"; | ||
|
||
export const func = onRequest( | ||
{ | ||
// Keep 5 instances warm | ||
minInstances: 5, | ||
}, | ||
(request, response) => { | ||
// ... | ||
} | ||
); | ||
``` | ||
|
||
## 7. Traffic Migration | ||
|
||
A human should follow these steps to migrate safely: | ||
|
||
> To migrate traffic safely: | ||
> | ||
> 1. Rename your new 2nd gen function with a different name. | ||
> 2. Comment out any existing `minInstances` or `maxInstances` config in the new 2nd gen function and instead set `maxInstances` to `1` while testing. | ||
> 3. Deploy it alongside the old 1st gen function. | ||
> 4. Gradually introduce traffic to the new function (e.g., via client-side changes or by calling it from the 1st gen function). | ||
> 5. As traffic ramps up to the new 2nd gen function, scale it up by adding back the original `minInstances` and `maxInstances` settings to the 2nd gen function. Reduce the `minInstances` and `maxInstances` settings for the 1st gen function as traffic decreases. | ||
> 6. The 1st gen function can be deleted once it has stopped receiving traffic. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
## Key Guidelines | ||
|
||
- Always use 2nd-gen functions for new development. | ||
- Use 1st-gen functions _only_ for Analytics and basic Auth triggers, since those aren't supported by 2nd gen. | ||
- Use `firebase-functions` SDK version 6.0.0 and above | ||
- Use top-level imports (e.g., `firebase-functions/https`). These are 2nd gen by default. If 1st gen is required (Analytics or basic Auth triggers), import from the `firebase-functions/v1` import path. | ||
|
||
## Configuration: Use Secret Params for API Keys | ||
|
||
For sensitive information like API keys (e.g., for LLMs, payment providers, etc.), **always** use `defineSecret`. This stores the value securely in Cloud Secret Manager. | ||
|
||
```typescript | ||
import { onRequest } from "firebase-functions/https"; | ||
import { logger } from "firebase-functions/logger"; | ||
import { defineString, defineSecret } from "firebase-functions/params"; | ||
|
||
// Securely define an LLM API key | ||
const LLM_API_KEY = defineSecret("LLM_API_KEY"); | ||
|
||
// Example function that uses the secret | ||
export const callLlm = onRequest({ secrets: [LLM_API_KEY] }, async (req, res) => { | ||
const apiKey = LLM_API_KEY.value(); | ||
|
||
// Use the apiKey to make a call to the LLM service | ||
logger.info("Calling LLM with API key."); | ||
|
||
// insert code here to call LLM... | ||
|
||
res.send("LLM API call initiated."); | ||
}); | ||
``` | ||
|
||
The CLI will prompt for 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> | ||
``` | ||
|
||
If you see an API key being accessed with `functions.config` in existing functions code, offer to upgrade to params. | ||
|
||
## Use the 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. | ||
|
||
1. **Install the SDK:** | ||
|
||
```bash | ||
npm i firebase-admin | ||
``` | ||
|
||
2. **Initialize in your code:** | ||
|
||
```typescript | ||
import * as admin from "firebase-admin"; | ||
import { onInit } from "firebase-functions"; | ||
|
||
onInit(() => { | ||
admin.initializeApp(); | ||
}); | ||
``` | ||
|
||
This should be done once at the top level of your `index.ts` file. | ||
|
||
## Common Imports | ||
|
||
```typescript | ||
import { onRequest, onCall, onCallGenkit } from "firebase-functions/https"; | ||
import { onDocumentUpdated } from "firebase-functions/firestore"; | ||
import { onNewFatalIssuePublished } from "firebase-functions/alerts/crashlytics"; | ||
import { onValueWritten } from "firebase-functions/database"; | ||
import { onSchedule } from "firebase-functions/scheduler"; | ||
const { onTaskDispatched } = require("firebase-functions/tasks"); | ||
import { onObjectFinalized } from "firebase-functions/storage"; | ||
import { onMessagePublished } from "firebase-functions/pubsub"; | ||
import { beforeUserSignedIn } from "firebase-functions/identity"; | ||
import { onTestMatrixCompleted } from "firebase-functions/testLab"; | ||
import { logger, onInit } from "firebase-functions"; | ||
import { defineString, defineSecret } from "firebase-functions/params"; | ||
``` | ||
|
||
A human can find code samples for these triggers in the [functions-samples repository](https://github.com/firebase/functions-samples/tree/main/Node). | ||
|
||
## 1st-gen Functions (Legacy Triggers) | ||
|
||
Use the `firebase-functions/v1` import for Analytics and basic Auth triggers. These aren't supported in 2nd gen. | ||
|
||
```typescript | ||
import * as functionsV1 from "firebase-functions/v1"; | ||
|
||
// v1 Analytics trigger | ||
export const onPurchase = functionsV1.analytics.event("purchase").onLog(async (event) => { | ||
logger.info("Purchase event", { value: event.params?.value }); | ||
}); | ||
|
||
// v1 Auth trigger | ||
export const onUserCreate = functionsV1.auth.user().onCreate(async (user) => { | ||
logger.info("User created", { uid: user.uid }); | ||
}); | ||
``` | ||
|
||
## Development Commands | ||
|
||
```bash | ||
# Install dependencies | ||
npm install | ||
|
||
# Compile TypeScript | ||
npm run build | ||
|
||
# Run emulators for local development | ||
# This is a long-running command. A human can run this command themselves to start the emulators: | ||
firebase emulators:start --only functions | ||
|
||
# Deploy functions | ||
jhuleatt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
firebase deploy --only functions | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
- Add LLM guidance (#1736) |
Uh oh!
There was an error while loading. Please reload this page.