{"id":48343702,"url":"https://github.com/firebase/firebase-functions-dart","last_synced_at":"2026-04-05T06:01:28.288Z","repository":{"id":333656943,"uuid":"1093614066","full_name":"firebase/firebase-functions-dart","owner":"firebase","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-31T16:43:33.000Z","size":794,"stargazers_count":11,"open_issues_count":9,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-31T18:35:33.647Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/firebase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-10T15:54:47.000Z","updated_at":"2026-03-31T16:30:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/firebase/firebase-functions-dart","commit_stats":null,"previous_names":["invertase/firebase_functions","firebase/firebase-functions-dart"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/firebase/firebase-functions-dart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase%2Ffirebase-functions-dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase%2Ffirebase-functions-dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase%2Ffirebase-functions-dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase%2Ffirebase-functions-dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/firebase","download_url":"https://codeload.github.com/firebase/firebase-functions-dart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/firebase%2Ffirebase-functions-dart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31426193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T02:22:46.605Z","status":"ssl_error","status_checked_at":"2026-04-05T02:22:33.263Z","response_time":75,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-05T06:01:27.600Z","updated_at":"2026-04-05T06:01:28.269Z","avatar_url":"https://github.com/firebase.png","language":"Dart","readme":"# Firebase SDK for Dart Functions\n\n[![Tests](https://github.com/invertase/firebase_functions/actions/workflows/test.yml/badge.svg)](https://github.com/invertase/firebase_functions/actions/workflows/test.yml)\n[![PR Checks](https://github.com/invertase/firebase_functions/actions/workflows/test.yml/badge.svg)](https://github.com/invertase/firebase_functions/actions/workflows/pr-checks.yml)\n\nWrite Firebase Cloud Functions in Dart with full type safety and performance.\n\n## Status: Alpha (v0.1.0)\n\nThis package provides a Dart implementation of Firebase Cloud Functions. Only HTTPS triggers are currently supported in production. Other trigger types are experimental and have varying levels of support.\n\n| Trigger Type | Status | Functions |\n|-------------|--------|-----------|\n| **HTTPS** | ✅ Production | `onRequest`, `onCall`, `onCallWithData` |\n| **Firestore** | ⚠️ Emulator only | `onDocumentCreated`, `onDocumentUpdated`, `onDocumentDeleted`, `onDocumentWritten`, `onDocumentCreatedWithAuthContext`, `onDocumentUpdatedWithAuthContext`, `onDocumentDeletedWithAuthContext`, `onDocumentWrittenWithAuthContext` |\n| **Realtime Database** | ⚠️ Emulator only | `onValueCreated`, `onValueUpdated`, `onValueDeleted`, `onValueWritten` |\n| **Storage** | ⚠️ Emulator only | `onObjectFinalized`, `onObjectArchived`, `onObjectDeleted`, `onObjectMetadataUpdated` |\n| **Pub/Sub** | 🚧 Experimental | `onMessagePublished` |\n| **Scheduler** | 🚧 Experimental | `onSchedule` |\n| **Firebase Alerts** | 🚧 Experimental | `onAlertPublished` and sub-namespace triggers |\n| **Eventarc** | 🚧 Experimental | `onCustomEventPublished` |\n| **Identity Platform** | 🚧 Experimental | `beforeUserCreated`, `beforeUserSignedIn` (+ `beforeEmailSent`, `beforeSmsSent`) |\n| **Remote Config** | 🚧 Experimental | `onConfigUpdated` |\n| **Test Lab** | 🚧 Experimental | `onTestMatrixCompleted` |\n| **Task Queues** | 🚧 Experimental | `onTaskDispatched` |\n\n\u003e **Legend**: ✅ Production — works in production and emulator. ⚠️ Emulator only — works with the Firebase emulator but not yet in production. 🚧 Experimental — implemented but not currently supported by the emulator or production; APIs may change.\n\n## Table of Contents\n\n- [Features](#features)\n- [Prerequisites](#prerequisites)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [HTTPS Functions](#https-functions)\n- [Pub/Sub Triggers](#pubsub-triggers)\n- [Firestore Triggers](#firestore-triggers)\n- [Realtime Database Triggers](#realtime-database-triggers)\n- [Storage Triggers](#storage-triggers)\n- [Scheduler Triggers](#scheduler-triggers)\n- [Firebase Alerts](#firebase-alerts)\n- [Identity Platform (Auth Blocking)](#identity-platform-auth-blocking)\n- [Remote Config](#remote-config)\n- [Test Lab](#test-lab)\n- [Parameters \u0026 Configuration](#parameters--configuration)\n- [Project Configuration](#project-configuration)\n- [Development](#development)\n\n## Features\n\n- **Type-safe**: Leverage Dart's strong type system with typed callable functions and CloudEvents\n- **Fast**: Compiled Dart code with efficient Shelf HTTP server\n- **Familiar API**: Similar to Firebase Functions Node.js SDK v2\n- **Streaming**: Server-Sent Events (SSE) support for callable functions\n- **Parameterized**: Deploy-time configuration with `defineString`, `defineInt`, `defineBoolean`\n- **Conditional Config**: CEL expressions for environment-based options\n- **Error Handling**: Built-in typed error classes matching the Node.js SDK\n- **Hot Reload**: Fast development with build_runner watch\n\n## Prerequisites\n\n- Dart SDK \u003e=3.0.0\n- Node v22 (Later versions of Node won't work!)\n- Java 21+ (for Firestore)\n- Custom Firebase CLI with Dart runtime support:\n\n```bash\ngit clone -b @invertase/dart https://github.com/invertase/firebase-tools.git\ncd firebase-tools\nnpm install\nnpm run build\nnpm link\n```\n\n## Installation\n\nAdd to your `pubspec.yaml`:\n\n```yaml\ndependencies:\n  firebase_functions:\n    path: ../firebase-functions-dart\n```\n\n## Quick Start\n\n```dart\nimport 'package:firebase_functions/firebase_functions.dart';\n\nvoid main(List\u003cString\u003e args) {\n  fireUp(args, (firebase) {\n    // Register your functions here\n  });\n}\n```\n\n## HTTPS Functions\n\n### onRequest - Raw HTTP Handler\n\n```dart\nfirebase.https.onRequest(\n  name: 'hello',\n  (request) async {\n    return Response.ok('Hello from Dart!');\n  },\n);\n```\n\n### onCall - Untyped Callable\n\n```dart\nfirebase.https.onCall(\n  name: 'greet',\n  (request, response) async {\n    final data = request.data as Map\u003cString, dynamic\u003e?;\n    final name = data?['name'] ?? 'World';\n    return CallableResult({'message': 'Hello, $name!'});\n  },\n);\n```\n\n### onCallWithData - Type-safe Callable\n\n```dart\nfirebase.https.onCallWithData\u003cGreetRequest, GreetResponse\u003e(\n  name: 'greetTyped',\n  fromJson: GreetRequest.fromJson,\n  (request, response) async {\n    return GreetResponse(message: 'Hello, ${request.data.name}!');\n  },\n);\n```\n\n### Streaming Support\n\n```dart\nfirebase.https.onCall(\n  name: 'countdown',\n  options: const CallableOptions(\n    heartBeatIntervalSeconds: HeartBeatIntervalSeconds(5),\n  ),\n  (request, response) async {\n    if (request.acceptsStreaming) {\n      for (var i = 10; i \u003e= 0; i--) {\n        await response.sendChunk({'count': i});\n        await Future.delayed(Duration(milliseconds: 100));\n      }\n    }\n    return CallableResult({'message': 'Countdown complete!'});\n  },\n);\n```\n\n### Error Handling\n\n```dart\nfirebase.https.onCall(\n  name: 'divide',\n  (request, response) async {\n    final data = request.data as Map\u003cString, dynamic\u003e?;\n    final a = data?['a'] as num?;\n    final b = data?['b'] as num?;\n\n    if (a == null || b == null) {\n      throw InvalidArgumentError('Both \"a\" and \"b\" are required');\n    }\n    if (b == 0) {\n      throw FailedPreconditionError('Cannot divide by zero');\n    }\n\n    return CallableResult({'result': a / b});\n  },\n);\n```\n\nAvailable error types: `InvalidArgumentError`, `FailedPreconditionError`, `NotFoundError`, `AlreadyExistsError`, `PermissionDeniedError`, `ResourceExhaustedError`, `UnauthenticatedError`, `UnavailableError`, `InternalError`, `DeadlineExceededError`, `CancelledError`.\n\n## Pub/Sub Triggers\n\n```dart\nfirebase.pubsub.onMessagePublished(\n  topic: 'my-topic',\n  (event) async {\n    final message = event.data;\n    print('ID: ${message?.messageId}');\n    print('Data: ${message?.textData}');\n    print('Attributes: ${message?.attributes}');\n  },\n);\n```\n\n## Firestore Triggers\n\n```dart\n// Document created\nfirebase.firestore.onDocumentCreated(\n  document: 'users/{userId}',\n  (event) async {\n    final data = event.data?.data();\n    print('Created: users/${event.params['userId']}');\n    print('Name: ${data?['name']}');\n  },\n);\n\n// Document updated\nfirebase.firestore.onDocumentUpdated(\n  document: 'users/{userId}',\n  (event) async {\n    final before = event.data?.before?.data();\n    final after = event.data?.after?.data();\n    print('Before: $before');\n    print('After: $after');\n  },\n);\n\n// Document deleted\nfirebase.firestore.onDocumentDeleted(\n  document: 'users/{userId}',\n  (event) async {\n    final data = event.data?.data();\n    print('Deleted data: $data');\n  },\n);\n\n// All write operations\nfirebase.firestore.onDocumentWritten(\n  document: 'users/{userId}',\n  (event) async {\n    final before = event.data?.before?.data();\n    final after = event.data?.after?.data();\n    // Determine operation type\n    if (before == null \u0026\u0026 after != null) print('CREATE');\n    if (before != null \u0026\u0026 after != null) print('UPDATE');\n    if (before != null \u0026\u0026 after == null) print('DELETE');\n  },\n);\n\n// Nested collections\nfirebase.firestore.onDocumentCreated(\n  document: 'posts/{postId}/comments/{commentId}',\n  (event) async {\n    print('Post: ${event.params['postId']}');\n    print('Comment: ${event.params['commentId']}');\n  },\n);\n\n// With auth context (identifies the principal that triggered the write)\nfirebase.firestore.onDocumentCreatedWithAuthContext(\n  document: 'orders/{orderId}',\n  (event) async {\n    print('Auth type: ${event.authType}');\n    print('Auth ID: ${event.authId}');\n    final data = event.data?.data();\n    print('Order: ${data?['product']}');\n  },\n);\n\nfirebase.firestore.onDocumentUpdatedWithAuthContext(\n  document: 'orders/{orderId}',\n  (event) async {\n    print('Updated by: ${event.authType} (${event.authId})');\n    final before = event.data?.before?.data();\n    final after = event.data?.after?.data();\n    print('Before: $before');\n    print('After: $after');\n  },\n);\n\nfirebase.firestore.onDocumentDeletedWithAuthContext(\n  document: 'orders/{orderId}',\n  (event) async {\n    print('Deleted by: ${event.authType} (${event.authId})');\n    final data = event.data?.data();\n    print('Deleted data: $data');\n  },\n);\n\nfirebase.firestore.onDocumentWrittenWithAuthContext(\n  document: 'orders/{orderId}',\n  (event) async {\n    print('Written by: ${event.authType} (${event.authId})');\n    final before = event.data?.before;\n    final after = event.data?.after;\n    if (before == null || !before.exists) print('CREATE');\n    else if (after == null || !after.exists) print('DELETE');\n    else print('UPDATE');\n  },\n);\n```\n\n## Realtime Database Triggers\n\nRespond to changes in Firebase Realtime Database. The `ref` parameter supports path wildcards (e.g., `{messageId}`) which are extracted into `event.params`.\n\n| Function | Triggers when | Event data |\n|----------|---------------|------------|\n| `onValueCreated` | Data is created | `DataSnapshot?` |\n| `onValueUpdated` | Data is updated | `Change\u003cDataSnapshot\u003e?` (before/after) |\n| `onValueDeleted` | Data is deleted | `DataSnapshot?` (deleted data) |\n| `onValueWritten` | Any write (create/update/delete) | `Change\u003cDataSnapshot\u003e?` (before/after) |\n\n### DataSnapshot API\n\nThe `DataSnapshot` class provides methods to inspect the data:\n\n- `val()` — Returns the snapshot contents (Map, List, String, num, bool, or null)\n- `exists()` — Returns `true` if the snapshot contains data\n- `child(path)` — Gets a child snapshot at the given path\n- `hasChild(path)` / `hasChildren()` — Check for child data\n- `numChildren()` — Number of child properties\n- `key` — Last segment of the reference path\n\n```dart\n// Value created\nfirebase.database.onValueCreated(\n  ref: 'messages/{messageId}',\n  (event) async {\n    final data = event.data?.val();\n    print('Created: ${event.params['messageId']}');\n    print('Data: $data');\n    print('Instance: ${event.instance}');\n  },\n);\n\n// Value updated — access before/after states\nfirebase.database.onValueUpdated(\n  ref: 'messages/{messageId}',\n  (event) async {\n    final before = event.data?.before?.val();\n    final after = event.data?.after?.val();\n    print('Before: $before');\n    print('After: $after');\n  },\n);\n\n// Value deleted\nfirebase.database.onValueDeleted(\n  ref: 'messages/{messageId}',\n  (event) async {\n    final data = event.data?.val();\n    print('Deleted: $data');\n  },\n);\n\n// All write operations — determine operation type from before/after\nfirebase.database.onValueWritten(\n  ref: 'users/{userId}/status',\n  (event) async {\n    final before = event.data?.before;\n    final after = event.data?.after;\n    if (before == null || !before.exists()) print('CREATE');\n    else if (after == null || !after.exists()) print('DELETE');\n    else print('UPDATE');\n  },\n);\n```\n\n### Database Instance Targeting\n\nUse `ReferenceOptions` to target a specific database instance:\n\n```dart\nfirebase.database.onValueCreated(\n  ref: 'messages/{messageId}',\n  options: const ReferenceOptions(instance: 'my-project-default-rtdb'),\n  (event) async {\n    print('Instance: ${event.instance}');\n  },\n);\n```\n\n## Storage Triggers\n\nRespond to changes in Cloud Storage objects. The `bucket` parameter specifies which storage bucket to watch.\n\n| Function | Triggers when |\n|----------|---------------|\n| `onObjectFinalized` | Object is created or overwritten |\n| `onObjectDeleted` | Object is permanently deleted |\n| `onObjectArchived` | Object is archived (versioned buckets) |\n| `onObjectMetadataUpdated` | Object metadata is updated |\n\n### StorageObjectData Properties\n\nThe event data provides full object metadata:\n\n- `name` — Object path within the bucket\n- `bucket` — Bucket name\n- `contentType` — MIME type\n- `size` — Content length in bytes\n- `storageClass` — Storage class (STANDARD, NEARLINE, COLDLINE, etc.)\n- `metadata` — User-provided key-value metadata\n- `timeCreated` / `updated` / `timeDeleted` — Timestamps\n- `md5Hash` / `crc32c` — Checksums\n- `generation` / `metageneration` — Versioning info\n\n```dart\n// Object finalized (created or overwritten)\nfirebase.storage.onObjectFinalized(\n  bucket: 'my-bucket',\n  (event) async {\n    final data = event.data;\n    print('Object finalized: ${data?.name}');\n    print('Content type: ${data?.contentType}');\n    print('Size: ${data?.size}');\n  },\n);\n\n// Object archived (versioned buckets only)\nfirebase.storage.onObjectArchived(\n  bucket: 'my-bucket',\n  (event) async {\n    final data = event.data;\n    print('Object archived: ${data?.name}');\n    print('Storage class: ${data?.storageClass}');\n  },\n);\n\n// Object deleted\nfirebase.storage.onObjectDeleted(\n  bucket: 'my-bucket',\n  (event) async {\n    final data = event.data;\n    print('Object deleted: ${data?.name}');\n  },\n);\n\n// Object metadata updated\nfirebase.storage.onObjectMetadataUpdated(\n  bucket: 'my-bucket',\n  (event) async {\n    final data = event.data;\n    print('Metadata updated: ${data?.name}');\n    print('Metadata: ${data?.metadata}');\n  },\n);\n```\n\n## Scheduler Triggers\n\nRun functions on a recurring schedule using Cloud Scheduler. The `schedule` parameter accepts standard Unix crontab expressions.\n\n### Cron Syntax\n\n```\n┌───────────── minute (0-59)\n│ ┌───────────── hour (0-23)\n│ │ ┌───────────── day of month (1-31)\n│ │ │ ┌───────────── month (1-12)\n│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)\n│ │ │ │ │\n* * * * *\n```\n\nCommon examples: `0 0 * * *` (daily midnight), `*/5 * * * *` (every 5 min), `0 9 * * 1-5` (weekdays 9 AM).\n\n### ScheduledEvent Properties\n\n- `jobName` — Cloud Scheduler job name (null if manually invoked)\n- `scheduleTime` — Scheduled execution time (RFC 3339 string)\n- `scheduleDateTime` — Parsed `DateTime` convenience getter\n\n```dart\n// Basic schedule — runs every day at midnight (UTC)\nfirebase.scheduler.onSchedule(\n  schedule: '0 0 * * *',\n  (event) async {\n    print('Job: ${event.jobName}');\n    print('Schedule time: ${event.scheduleTime}');\n  },\n);\n```\n\n### Timezone and Retry Configuration\n\nUse `ScheduleOptions` to set a timezone and configure retry behavior for failed invocations:\n\n```dart\nfirebase.scheduler.onSchedule(\n  schedule: '0 9 * * 1-5',\n  options: const ScheduleOptions(\n    timeZone: TimeZone('America/New_York'),\n    retryConfig: RetryConfig(\n      retryCount: RetryCount(3),\n      maxRetrySeconds: MaxRetrySeconds(60),\n      minBackoffSeconds: MinBackoffSeconds(5),\n      maxBackoffSeconds: MaxBackoffSeconds(30),\n    ),\n    memory: Memory(MemoryOption.mb256),\n  ),\n  (event) async {\n    print('Executed at: ${event.scheduleDateTime}');\n  },\n);\n```\n\n| RetryConfig field | Description |\n|---|---|\n| `retryCount` | Number of retry attempts |\n| `maxRetrySeconds` | Maximum total time for retries |\n| `minBackoffSeconds` | Minimum wait before retry (0-3600) |\n| `maxBackoffSeconds` | Maximum wait before retry (0-3600) |\n| `maxDoublings` | Times to double backoff before going linear |\n\n## Firebase Alerts\n\n```dart\n// App Distribution new tester iOS device\nfirebase.alerts.appDistribution.onNewTesterIosDevicePublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('New tester iOS device:');\n    print('  Tester: ${payload?.testerName} (${payload?.testerEmail})');\n    print('  Device: ${payload?.testerDeviceModelName}');\n    print('  Identifier: ${payload?.testerDeviceIdentifier}');\n  },\n);\n\n// Crashlytics fatal issues\nfirebase.alerts.crashlytics.onNewFatalIssuePublished(\n  (event) async {\n    final issue = event.data?.payload.issue;\n    print('Issue: ${issue?.title}');\n    print('App: ${event.appId}');\n  },\n);\n\n// Crashlytics ANR (Application Not Responding) issues\nfirebase.alerts.crashlytics.onNewAnrIssuePublished(\n  (event) async {\n    final issue = event.data?.payload.issue;\n    print('ANR issue: ${issue?.title}');\n    print('App: ${event.appId}');\n  },\n);\n\n// Crashlytics regression alerts\nfirebase.alerts.crashlytics.onRegressionAlertPublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('Regression: ${payload?.type}');\n    print('Issue: ${payload?.issue.title}');\n    print('Resolved: ${payload?.resolveTime}');\n  },\n);\n\n// Crashlytics non-fatal issues\nfirebase.alerts.crashlytics.onNewNonfatalIssuePublished(\n  (event) async {\n    final issue = event.data?.payload.issue;\n    print('Non-fatal issue: ${issue?.title}');\n    print('App: ${event.appId}');\n  },\n);\n\n// Crashlytics stability digest\nfirebase.alerts.crashlytics.onStabilityDigestPublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('Stability digest: ${payload?.digestDate}');\n    print('Trending issues: ${payload?.trendingIssues.length ?? 0}');\n  },\n);\n\n// Crashlytics velocity alerts\nfirebase.alerts.crashlytics.onVelocityAlertPublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('Velocity alert: ${payload?.issue.title}');\n    print('Crash count: ${payload?.crashCount}');\n    print('Percentage: ${payload?.crashPercentage}%');\n    print('First version: ${payload?.firstVersion}');\n  },\n);\n\n// Billing plan updates\nfirebase.alerts.billing.onPlanUpdatePublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('New Plan: ${payload?.billingPlan}');\n    print('Updated By: ${payload?.principalEmail}');\n  },\n);\n\n// Billing automated plan updates\nfirebase.alerts.billing.onPlanAutomatedUpdatePublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('Automated plan update:');\n    print('  Plan: ${payload?.billingPlan}');\n    print('  Type: ${payload?.notificationType}');\n  },\n);\n\n// Performance threshold alerts\nfirebase.alerts.performance.onThresholdAlertPublished(\n  options: const AlertOptions(appId: '1:123456789:ios:abcdef'),\n  (event) async {\n    final payload = event.data?.payload;\n    print('Metric: ${payload?.metricType}');\n    print('Threshold: ${payload?.thresholdValue}');\n    print('Actual: ${payload?.violationValue}');\n  },\n);\n\n// App Distribution in-app feedback\nfirebase.alerts.appDistribution.onInAppFeedbackPublished(\n  (event) async {\n    final payload = event.data?.payload;\n    print('In-app feedback:');\n    print('  Tester: ${payload?.testerEmail}');\n    print('  App version: ${payload?.appVersion}');\n    print('  Text: ${payload?.text}');\n    print('  Console: ${payload?.feedbackConsoleUri}');\n  },\n);\n```\n\n## Eventarc\n\n```dart\n// Custom event (default Firebase channel)\nfirebase.eventarc.onCustomEventPublished(\n  eventType: 'com.example.myevent',\n  (event) async {\n    print('Event: ${event.type}');\n    print('Source: ${event.source}');\n    print('Data: ${event.data}');\n  },\n);\n\n// With channel and filters\nfirebase.eventarc.onCustomEventPublished(\n  eventType: 'com.example.filtered',\n  options: const EventarcTriggerOptions(\n    channel: 'my-channel',\n    filters: {'category': 'important'},\n  ),\n  (event) async {\n    print('Event: ${event.type}');\n    print('Data: ${event.data}');\n  },\n);\n```\n\n## Identity Platform (Auth Blocking)\n\n```dart\n// Before user created\nfirebase.identity.beforeUserCreated(\n  options: const BlockingOptions(idToken: true, accessToken: true),\n  (AuthBlockingEvent event) async {\n    final user = event.data;\n\n    // Block certain email domains\n    if (user?.email?.endsWith('@blocked.com') ?? false) {\n      throw PermissionDeniedError('Email domain not allowed');\n    }\n\n    // Set custom claims\n    if (user?.email?.endsWith('@admin.com') ?? false) {\n      return const BeforeCreateResponse(\n        customClaims: {'admin': true},\n      );\n    }\n\n    return null;\n  },\n);\n\n// Before user signed in\nfirebase.identity.beforeUserSignedIn(\n  options: const BlockingOptions(idToken: true),\n  (AuthBlockingEvent event) async {\n    return BeforeSignInResponse(\n      sessionClaims: {\n        'lastLogin': DateTime.now().toIso8601String(),\n        'signInIp': event.ipAddress,\n      },\n    );\n  },\n);\n```\n\n\u003e **Note**: `beforeEmailSent` and `beforeSmsSent` are also available but cannot be tested with the Firebase Auth emulator (emulator only supports `beforeUserCreated` and `beforeUserSignedIn`). They work in production deployments.\n\n## Remote Config\n\nTrigger a function when Firebase Remote Config is updated.\n\n```dart\nfirebase.remoteConfig.onConfigUpdated((event) async {\n  final data = event.data;\n  print('Remote Config updated:');\n  print('  Version: ${data?.versionNumber}');\n  print('  Description: ${data?.description}');\n  print('  Update Origin: ${data?.updateOrigin.value}');\n  print('  Update Type: ${data?.updateType.value}');\n  print('  Updated By: ${data?.updateUser.email}');\n});\n```\n\n## Test Lab\n\nTrigger a function when a Firebase Test Lab test matrix completes.\n\n```dart\nfirebase.testLab.onTestMatrixCompleted((event) async {\n  final data = event.data;\n  print('Test matrix completed:');\n  print('  Matrix ID: ${data?.testMatrixId}');\n  print('  State: ${data?.state.value}');\n  print('  Outcome: ${data?.outcomeSummary.value}');\n  print('  Client: ${data?.clientInfo.client}');\n  print('  Results URI: ${data?.resultStorage.resultsUri}');\n});\n```\n\n## Parameters \u0026 Configuration\n\n### Defining Parameters\n\n```dart\nfinal welcomeMessage = defineString(\n  'WELCOME_MESSAGE',\n  ParamOptions(\n    defaultValue: 'Hello from Dart!',\n    label: 'Welcome Message',\n    description: 'The greeting message returned by the function',\n  ),\n);\n\nfinal minInstances = defineInt(\n  'MIN_INSTANCES',\n  ParamOptions(defaultValue: 0),\n);\n\nfinal isProduction = defineBoolean(\n  'IS_PRODUCTION',\n  ParamOptions(defaultValue: false),\n);\n```\n\n### Using Parameters at Runtime\n\n```dart\nfirebase.https.onRequest(\n  name: 'hello',\n  (request) async {\n    return Response.ok(welcomeMessage.value());\n  },\n);\n```\n\n### Using Parameters in Options (Deploy-time)\n\n```dart\nfirebase.https.onRequest(\n  name: 'configured',\n  options: HttpsOptions(\n    minInstances: DeployOption.param(minInstances),\n  ),\n  handler,\n);\n```\n\n### Conditional Configuration\n\n```dart\nfirebase.https.onRequest(\n  name: 'api',\n  options: HttpsOptions(\n    // 2GB in production, 512MB in development\n    memory: Memory.expression(isProduction.thenElse(2048, 512)),\n  ),\n  (request) async {\n    final env = isProduction.value() ? 'production' : 'development';\n    return Response.ok('Running in $env mode');\n  },\n);\n```\n\n## Project Configuration\n\nYour `firebase.json` must specify the Dart runtime:\n\n```json\n{\n  \"functions\": [\n    {\n      \"source\": \".\",\n      \"codebase\": \"default\",\n      \"runtime\": \"dart3\"\n    }\n  ],\n  \"emulators\": {\n    \"functions\": { \"port\": 5001 },\n    \"firestore\": { \"port\": 8080 },\n    \"database\": { \"port\": 9000 },\n    \"auth\": { \"port\": 9099 },\n    \"pubsub\": { \"port\": 8085 },\n    \"ui\": { \"enabled\": true, \"port\": 4000 }\n  }\n}\n```\n\n## Development\n\n### Running the Emulator\n\n```bash\nfirebase emulators:start\n```\n\n### Building\n\n```bash\ndart run build_runner build\n```\n\n### Testing\n\nRun all tests:\n```bash\ndart test\n```\n\nRun specific test suites:\n```bash\n# Unit tests only\ndart test --exclude-tags=snapshot,integration\n\n# Builder tests\ndart run build_runner build --delete-conflicting-outputs\ndart test test/builder/\n\n# Snapshot tests (compare with Node.js SDK)\ndart test test/snapshots/\n```\n\nSee [Testing Guide](test/snapshots/README.md) for more details.\n\n## Documentation\n\n- [Getting Started](docs/getting-started.md)\n- [HTTPS Triggers](docs/https-triggers.md)\n- [Pub/Sub Triggers](docs/pubsub-triggers.md)\n- [Architecture](docs/architecture.md)\n\n## License\n\nApache 2.0\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirebase%2Ffirebase-functions-dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffirebase%2Ffirebase-functions-dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffirebase%2Ffirebase-functions-dart/lists"}