iTranslated by AI
Running Firestore Trigger Functions in Dart Leveraging GCP
About This Article
In this article, I will explain how to run Firestore trigger functions using Dart by leveraging various GCP services.
In my personal projects, I often develop applications using Firebase as the backend. Until now, I have implemented the necessary server-side logic in TypeScript (JavaScript) and deployed it to Cloud Functions.
As a Flutter and Dart engineer, I love the Dart language and have always wished I could implement the server-side in Dart as well. I have explored a way to make this a reality, so I will summarize it here.
By utilizing GCP services such as Cloud Run, Eventarc, Secret Manager, and Workload Identity, we aim to:
- Write and deploy HTTP functions in Dart
- Write and deploy Firestore trigger functions in Dart
- Build a reasonably comfortable deployment workflow using GitHub Actions
Here is the sample repository:
Although simple, the following architecture diagram illustrates what we will cover:

Please note the following:
- The dart_firebase_admin package introduced in this document is still in its early stages and may contain bugs or missing features; sometimes you may need to track down and resolve these bugs yourself.
- You will need to manually write deployment tasks using
gcloudcommands, which are usually highly abstracted within the Firebase CLI.
A certain level of patience and a pioneering spirit will be required.
Therefore, at this point, I am not strongly recommending that "all future server-side logic for Firebase should be written in Dart!"
However, personally, I intend to actively write server-side logic for Firebase in Dart while exploring features I haven't fully tested yet. I believe it has already reached a level where it is practical to use.
Cloud Run
Here is a description of Cloud Run:
Cloud Run is a managed computing platform that lets you run containers directly on top of Google's scalable infrastructure.
You can deploy code written in any programming language to Cloud Run if you can build a container image from it. Building container images is optional. If you are using Go, Node.js, Python, Java, .NET Core, or Ruby, you can use the source-based deployment option that builds the container for you, using the best practices for the language you're using.
As introduced at Google I/O 2023 (though it reached GA in 2022), Cloud Functions (2nd Gen) actually runs on Cloud Run behind the scenes.
There isn't a huge difference in functionality compared to using Cloud Functions (1st Gen).
Reference:
The important thing is that because of Cloud Run's characteristic that "any code written in any programming language can be deployed as long as a container image can be built," we can deploy and run functions (services) written in Dart on Cloud Run.
dart_firebase_admin Package
dart_firebase_admin is a package developed by Invertase, a well-known name among Dart and Flutter engineers, which enables the use of the Firebase Admin SDK for Dart.
The scope of functionality provided by the dart_firebase_admin package roughly includes:
- Initialization of
adminAppwith Admin privileges using the Firebase Admin SDK - API calls for various Firebase features through the Firebase API for Dart (see also the firebaseapis package) using Admin privileges
You can use features such as Cloud Firestore, Firebase Auth, and Firebase Messaging with Admin privileges as follows:
final adminApp = FirebaseAdminApp.initializeApp(
'your-project-id',
Credential.fromServiceAccountParams(
clientId: 'your-client-id',
privateKey: 'your-private-key',
email: 'your-email',
),
);
final firestore = Firestore(adminApp);
final auth = Auth(adminApp);
final messaging = Messaging(adminApp);
functions_framework Package
The functions_framework package is developed by the GoogleCloudPlatform organization and provides a mechanism and samples for writing in Dart and deploying to platforms such as Cloud Run.
Below, we will finally introduce how to deploy functions written in Dart to Cloud Run, referring to the official samples from the functions_framework package.
Implementing an HTTP Function in Dart and Deploying to Cloud Run
We will write a function that can later be deployed to Cloud Run and used as an HTTP endpoint as shown below.
The shelf package, a web server framework for Dart, is also imported to use its Response and Request types.
import 'package:functions_framework/functions_framework.dart';
import 'package:shelf/shelf.dart';
@CloudFunction()
Response hello(Request request) => Response.ok('Hello, World!');
The above returns a "Hello, World!" message with a status code of 200 when requested using the function (service name) hello.
The @CloudFunction() annotation above is provided by the functions_framework package.
By running code generation with build_runner:
dart pub run build_runner build --delete-conflicting-outputs
A file like the following will be generated in bin/server.dart.
The _nameToFunctionTarget function is generated along with the main function, which is the entry point for Dart.
The _nameToFunctionTarget function is structured to call the function corresponding to the function name received as an argument using a switch statement.
When the argument is the string 'hello', the hello function (function_library.hello) defined earlier is called.
import 'package:functions_framework/serve.dart';
import 'package:hello_world_function/functions.dart' as function_library;
Future<void> main(List<String> args) async {
await serve(args, _nameToFunctionTarget);
}
FunctionTarget? _nameToFunctionTarget(String name) => switch (name) {
'hello' => FunctionTarget.http(function_library.hello),
_ => null
};
Running this Dart file as follows will start the web server, which by default accepts requests on port 8080.
$ dart run bin/server.dart
Listening on :8080
Trying a request with the curl command confirms that it indeed returns a "Hello World!" response.
$ curl http://localhost:8080
Hello, World!
Now, to deploy this Dart web server to Cloud Run, we use a Dockerfile like the following.
The following steps are performed:
- Specify a lightweight image with the Dart environment set up using
FROM dart:stable AS buildat the beginning. - Execute build_runner to perform code generation.
- Compile the specified Dart file into an executable using the
dart compile execommand. - Specify the generated executable as the entry point, and start the web server on port 8080 by specifying the function name in the
--targetoption andhttpin the--signature-typeoption.
The content is quite simple.
FROM dart:stable AS build
# Resolve app dependencies.
WORKDIR /app
COPY pubspec.* ./
RUN dart pub get
# Copy app source code and AOT compile it.
COPY . .
# Ensure packages are still up-to-date if anything has changed
RUN dart pub get --offline
RUN dart pub run build_runner build --delete-conflicting-outputs
RUN dart compile exe bin/server.dart -o bin/server
# Build minimal serving image from AOT-compiled `/server` and required system
# libraries and configuration files stored in `/runtime/` from the build stage.
FROM scratch
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/bin/
# Start server.
EXPOSE 8080
ENTRYPOINT ["/app/bin/server", "--target=hello", "--signature-type=http"]
To deploy, execute the following command from the gcloud CLI.
By specifying the location of the Dockerfile in the source option and setting --platform=managed (the default value), you can deploy the function to Cloud Run with those settings and configuration.
gcloud run deploy hello \
--source=. \ # can use $PWD or . for current dir
--platform=managed \ # for Cloud Run
--allow-unauthenticated # for public access
We have now successfully deployed the HTTP function written in Dart to Cloud Run.
Let's try requesting the actually deployed HTTP function with the curl command.
Note that this is a public function and can be requested by anyone.
$ curl https://hello-<string-corresponding-to-generated-url>-an.a.run.app
Hello, World!
Implementing a Cloud Event Trigger Function in Dart and Deploying to Cloud Run
Cloud Run allows you to deploy not only HTTP functions but also functions triggered by Cloud Events.
Later, we will make it possible to forward events such as Create, Update, Delete, and Write for Firestore documents to the function deployed to Cloud Run using this method.
The functions_framework package also provides an easy way to write functions triggered by such Cloud Events.
Just as with the definition of an HTTP function, use the @CloudFunction() annotation and simply specify CloudEvent event and RequestContext context as arguments.
The content of the forwarded Cloud Event is contained in the event parameter of the CloudEvent type.
@CloudFunction()
void oncloudevent(CloudEvent event, RequestContext context) {
context.logger.info('[CloudEvent] source: ${event.source}, subject: ${event.subject}');
}
The Dockerfile for deployment is as follows, and the only difference from the HTTP function is that cloudevent is specified for the --signature-type option.
FROM dart:stable AS build
# Resolve app dependencies.
WORKDIR /app
COPY pubspec.* ./
RUN dart pub get
# Copy app source code and AOT compile it.
COPY . .
# Ensure packages are still up-to-date if anything has changed
RUN dart pub get --offline
RUN dart pub run build_runner build --delete-conflicting-outputs
RUN dart compile exe bin/server.dart -o bin/server
# Build minimal serving image from AOT-compiled `/server` and required system
# libraries and configuration files stored in `/runtime/` from the build stage.
FROM scratch
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/bin/
# Start server.
EXPOSE 8080
ENTRYPOINT ["/app/bin/server", "--target=oncloudevent", "--signature-type=cloudevent"]
The deployment command is also almost the same.
Cloud Events are generated from other services within the same GCP project and do not need to be made public, so the --no-allow-unauthenticated option is provided.
gcloud run deploy oncloudevent \
--source=. \ # can use $PWD or . for current dir
--platform=managed \ # for Cloud Run
--no-allow-unauthenticated # for restricted access
Forwarding Firestore Events to Cloud Run with Eventarc
Quoting the description of Eventarc:
Eventarc lets you build event-driven architectures without having to implement, customize, or maintain the underlying infrastructure. Eventarc offers a standardized solution to manage the state changes, called events, between decoupled microservices. When triggered, Eventarc routes these events to various destinations (see Event destinations in this document) while managing delivery, security, authorization, observability, and error handling for you.
Eventarc provides event capabilities available across various GCP sources.
By using Eventarc, you can forward events in CloudEvents format from one service (event provider) to another (event receiver/consumer).
CloudEvents is a standard specification for describing such event metadata.
For example, using the gcloud CLI, you can create a trigger that:
- For the
foos/{fooId}document in the(default)database of Cloud Firestore - Takes a document creation event (
google.cloud.firestore.document.v1.created) - And forwards the event to the
oncloudeventservice (function) in Cloud Run.
gcloud eventarc triggers create oncloudevent \
--destination-run-service=oncloudevent \
--event-filters="type=google.cloud.firestore.document.v1.created" \
--event-filters="database=(default)" \
--event-filters="namespace=(default)" \
--event-filters-path-pattern="document=foos/{fooId}" \
--event-data-content-type="application/protobuf" \
--service-account="your-service-account-name@project-id.iam.gserviceaccount.com" \
Of course, you can easily perform the equivalent operation through the Google Cloud Console GUI, or manage it using the Eventarc API.
Secret Manager
Quoting the description of Secret Manager:
Secret Manager allows you to store, manage, and access secrets as binary blobs or text strings. With the appropriate permissions, you can view the contents of the secret.
Secret Manager is useful for storing configuration information that an application needs at runtime, such as database passwords, API keys, and TLS certificates.
It is recommended to use Secret Manager to manage and access sensitive information (such as private keys) required for using the Firebase Admin SDK in functions deployed to Cloud Run.
When deploying a service to Cloud Run, you can set the sensitive information that the Cloud Run service should reference by listing the key names, values, and versions in the --set-secrets option, as shown below:
gcloud run deploy hello \
--source=. \ # can use $PWD or . for current dir
--platform=managed \ # for Cloud Run
--no-allow-unauthenticated \ # for restricted access
--set-secrets=PROJECT_ID=PROJECT_ID:latest,CLIENT_ID=CLIENT_ID:latest,CLIENT_EMAIL=CLIENT_EMAIL:latest,PRIVATE_KEY=PRIVATE_KEY:latest
Workload Identity Federation
Quoting the description of Workload Identity Federation:
This document describes how to use identity federation for external workloads. Identity federation lets you use Identity and Access Management (IAM) to grant external identities permissions to act as a service account, including the ability to impersonate the service account. This eliminates the maintenance and security burden associated with service account keys.
You can use identity federation with Amazon Web Services (AWS), or with any identity provider (IdP) that supports OpenID Connect (OIDC) (such as Microsoft Azure), or SAML 2.0.
Applications running outside of Google Cloud can use service account keys to access Google Cloud resources. However, service account keys are powerful credentials and can present a security risk if not managed correctly.
By using identity federation, you can use Identity and Access Management (IAM) to grant external identities roles, including the ability to impersonate a service account. This eliminates the maintenance and security burden associated with service account keys.
Articles like the one below are very helpful for specific configuration details.
By performing Workload Identity Federation, you can configure it so that specified operations can only be performed from GitHub Actions of a specific repository, without issuing a service account key.
jobs:
deploy:
steps:
# Omitted
- name: Google Auth
id: auth
uses: 'google-github-actions/auth@v2'
with:
project_id: ${{ secrets.PROJECT_ID }}
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.SERVICE_ACCOUNT }}
Building a Deployment Workflow with GitHub Actions
When writing Firebase Functions using the Node.js SDK in the traditional way, you can deploy functions by running the firebase deploy --only functions(:function_name) command from the Firebase CLI.
Also, if it's a Firestore trigger function, it will be deployed as a function triggered by the creation of a document in the foos collection, provided it is written using the firebase-functions package as shown below.
import * as functions from 'firebase-functions'
export const onCreateSubmission = functions
.region(`asia-northeast1`)
.firestore.document(`/foos/{fooId}`)
.onCreate(async (snapshot, context) => {
/** Omitted */
})
When writing functions in Dart and deploying them to Cloud Run, the Firebase CLI cannot be used, and there is no equivalent to the firebase-functions package.
Therefore, I will show an example of building a reasonably comfortable deployment workflow using GitHub Actions here.
Some specific details are collapsed, so please expand them as needed.
You can check the entire workflow sample here:
In the workflow, we will do the following:
- Deploy functions (HTTP or Firestore triggers) written in Dart to Cloud Run using the gcloud CLI.
- If it is a Firestore trigger, create the corresponding Eventarc trigger.
- Delete functions that are no longer needed (deployed to Cloud Run but no longer present locally).
First, we will list the signature_type, and in the case of a Firestore trigger function, the event_type and path_pattern along with the function name in .github/actions/services.yml as follows.
services:
- service: hello
signature_type: http
- service: oncloudevent
signature_type: cloudevent
event_type: google.cloud.firestore.document.v1.created
path_pattern: document=foos/{fooId}
The above services.yml is parsed in deploy.yml as shown below, stored in $GITHUB_OUTPUT, and passed to the services of reusable_deploy_workflow.yml.
jobs:
set_services:
runs-on: ubuntu-latest
outputs:
services: ${{ steps.set-services.outputs.services }}
steps:
- uses: actions/checkout@v4
- name: Set services
id: set-services
run: |
services=$(cat ${{ github.workspace }}/.github/actions/services.yml | ruby -ryaml -rjson -e 'puts YAML.load(STDIN.read)["services"].to_json')
echo $services
echo "services=${services}" >> $GITHUB_OUTPUT
call_workflow:
needs: set_services
uses: ./reusable_deploy_workflow.yml
secrets: inherit
permissions:
contents: 'read'
id-token: 'write'
with:
services: ${{ needs.set_services.outputs.services }}
In reusable_deploy_workflow.yml, which is called from deploy.yml, the contents of .github/actions/services.yml are reflected in strategy.matrix as follows:
on:
workflow_call:
inputs:
services:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(inputs.services) }}
As preparation, set up the repository and Dart environment, authenticate using Workload Identity with google-github-actions/auth@v2, and complete the gcloud SDK setup with google-github-actions/setup-gcloud@v2 as follows:
# Omitted
jobs:
deploy:
# Omitted
steps:
- name: Checkout
uses: 'actions/checkout@v4'
- name: Set up Dart
uses: 'dart-lang/setup-dart@v1'
with:
sdk: stable
- name: Google Auth
id: auth
uses: 'google-github-actions/auth@v2'
with:
project_id: ${{ secrets.PROJECT_ID }}
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.SERVICE_ACCOUNT }}
- name: Set up Cloud SDK
uses: 'google-github-actions/setup-gcloud@v2'
Normally, one Dockerfile is required for each service (function) deployed to Cloud Run, but I've adopted a mechanism to dynamically generate a Dockerfile from Dockerfile.template during the GitHub Actions CI.
The dynamic values to be provided to Dockerfile.template are the service name (matrix.service) and the signature type (matrix.signature_type), which indicates whether it is an HTTP function or a Cloud Event trigger.
The specified placeholder parts are replaced with the sed command during CI.
# Omitted
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(inputs.services) }}
steps:
# Omitted
- name: Generate Dockerfile
run: |
rm -f Dockerfile
sed -e "s/TARGET_PLACEHOLDER/${{ matrix.service }}/" \
-e "s/SIGNATURE_TYPE_PLACEHOLDER/${{ matrix.signature_type }}/" \
Dockerfile.template > Dockerfile
cat Dockerfile
Then, deploy the function to Cloud Run using google-github-actions/deploy-cloudrun@v2. This is equivalent to executing the gcloud run deploy command.
# Omitted
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(inputs.services) }}
steps:
# Omitted
- name: Deploy to Cloud Run
id: deploy
uses: 'google-github-actions/deploy-cloudrun@v2'
with:
source: ./
service: ${{ matrix.service }}
region: ${{ env.REGION }}
flags: ${{ env.FLAGS }}
env_vars: key=value
secrets: key=value:latest
If the deployed function is a Firestore trigger, we create a corresponding Eventarc trigger.
Also, to skip if the corresponding trigger has already been created, we use gcloud eventarc triggers describe to check the status of existing triggers.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Omitted
- name: Create Eventarc Trigger
if: ${{ matrix.signature_type == 'cloudevent' }}
run: |
if ! gcloud eventarc triggers describe ${{ matrix.service }} --location=${{ env.REGION }} --project=${{ secrets.PROJECT_ID }} > /dev/null 2>&1; then
echo "Trigger does not exist. Creating trigger."
gcloud eventarc triggers create ${{ matrix.service }} \
--location=${{ env.REGION }} \
--destination-run-service=${{ matrix.service }} \
--event-filters="type=${{ matrix.event_type }}" \
--event-filters="database=(default)" \
--event-filters="namespace=(default)" \
--event-filters-path-pattern="${{ matrix.path_pattern }}" \
--event-data-content-type="application/protobuf" \
--service-account="${{ secrets.EVENTARC_SERVICE_ACCOUNT }}" \
--project=${{ secrets.PROJECT_ID }}
else
echo "Trigger already exists. Skipping creation."
fi
Furthermore, as a cleanup job following the deploy job, we handle the deletion of functions that are already deployed to Cloud Run but are no longer included in .github/actions/services.yml.
Deployed functions can be listed using gcloud run services list --platform managed.
jobs:
deploy:
# Omitted
cleanup:
needs: deploy
runs-on: ubuntu-latest
steps:
# Omitted
- name: Cleanup Services
run: |
services=$(echo '${{ inputs.services }}' | ruby -rjson -e 'puts JSON.parse(STDIN.read).map { |s| s["service"] }.join(" ")')
deployed_services=$(gcloud run services list --platform managed --region ${{ env.REGION }} --project ${{ secrets.PROJECT_ID }} --format="value(name)")
services_to_delete=()
for service in $deployed_services; do
if [[ ! $services =~ $service ]]; then
services_to_delete+=($service)
fi
done
if [ ${#services_to_delete[@]} -eq 0 ]; then
echo "No services to delete."
else
echo "Services to delete: ${services_to_delete[@]}"
for service in "${services_to_delete[@]}"; do
echo "Deleting service: $service"
gcloud run services delete $service \
--platform managed \
--region ${{ env.REGION }} \
--project ${{ secrets.PROJECT_ID }} \
--quiet
done
fi
Practical Examples
HTTP Function: Integrating Firebase Auth and LINE Login via Custom Token Authentication
I personally implement this often and have even written a summary article on Zenn. As a practical example of an HTTP function, I will introduce a typical implementation required on the server side to integrate Firebase Auth and LINE Login using custom token authentication.
Note that there is a bug in the createCustomToken method provided by the dart_firebase_admin package where the format of the custom token is incorrect. I actually created a PR for this just the other day.
The PR review is delayed and it hasn't been merged yet, but it has been confirmed by others who reported the same issue that this resolves it.
Define the HTTP function in lib/functions.dart using @CloudFunction().
Since Cloud Run has constraints such as not allowing uppercase letters in function names, I have used all lowercase for the function name.
@CloudFunction()
Future<Response> createfirebaseauthcustomtoken(Request request) =>
CreateFirebaseAuthCustomTokenFunction(
firestore: firestore,
auth: auth,
request: request,
).call();
The firestore and auth instances are provided by the dart_firebase_admin package to enable Cloud Firestore and Firebase Auth features using an initialized AdminApp as shown below.
final environmentVariable = EnvironmentVariable(EnvironmentProvider());
final _adminApp = FirebaseAdminApp.initializeApp(
environmentVariable.projectId,
Credential.fromServiceAccountParams(
clientId: environmentVariable.clientId,
privateKey: environmentVariable.privateKey,
email: environmentVariable.clientEmail,
),
);
final firestore = Firestore(_adminApp);
final auth = Auth(_adminApp);
The following is the logic for integrating Firebase Auth and LINE Login via custom token authentication.
While the contents of the private methods are omitted, the server-side logic is implemented in Dart in a way that corresponds to the TypeScript implementation in the article mentioned above.
class CreateFirebaseAuthCustomTokenFunction {
const CreateFirebaseAuthCustomTokenFunction({
required this.firestore,
required this.auth,
required this.request,
});
final Firestore firestore;
final Auth auth;
final Request request;
/// Integrates Firebase Auth and LINE Login via custom token authentication.
///
/// Verifies the `accessToken` obtained from the request body of [request] using the
/// [_verifyAccessToken] method, and retrieves the LINE profile using the [_getLineProfile] method.
///
/// Subsequently, it creates a custom token from the LINE user ID using Firebase Auth,
/// saves the profile information to Firestore, and returns the custom token.
Future<Response> call() async {
try {
final json =
jsonDecode(await request.readAsString()) as Map<String, dynamic>;
final accessToken = json['accessToken'] as String?;
if (accessToken == null) {
return Response.badRequest(
body: jsonEncode({'message': 'accessToken is required.'}),
);
}
await _verifyAccessToken(accessToken);
final profile = await _getLineProfile(accessToken);
final customToken = await auth.createCustomToken(profile.lineUserId);
await firestore.collection('users').doc(profile.lineUserId).set({
'displayName': profile.displayName,
'imageUrl': profile.imageUrl,
});
return Response.ok(jsonEncode({'customToken': customToken}));
} on Exception catch (e) {
return Response.badRequest(body: jsonEncode({'message': e.toString()}));
}
}
Future<void> _verifyAccessToken(String accessToken) async { /** Omitted */ }
Future<({String lineUserId, String displayName, String? imageUrl})>
_getLineProfile(String accessToken) async { /** Omitted */ }
}
Firestore Trigger Function: Updating a Document Triggered by Document Creation
Next, as an example of a Firestore trigger function, let's implement a function that fires when a document is created in the submissions collection (assuming submission data such as requests for approval or expense claims). If that document contains a submittedByUserId (submitter's ID) field, the isVerified field will be updated to true.
Define a function that takes CloudEvent as an argument using @CloudFunction() in lib/functions.dart.
@CloudFunction()
Future<void> oncreatesubmission(CloudEvent event, RequestContext context) =>
OnCreateSubmissionFunction(
firestore: firestore,
auth: auth,
event: event,
context: context,
).call();
The following is the content of the process.
You can write logic using the Admin SDK with almost the same feel as the client SDK.
class OnCreateSubmissionFunction {
const OnCreateSubmissionFunction({
required this.firestore,
required this.auth,
required this.event,
required this.context,
});
final Firestore firestore;
final Auth auth;
final CloudEvent event;
final RequestContext context;
/// Called when a new document is created in the `submissions` collection.
///
/// If the document contains a non-null `submittedByUserId`, the `isVerified`
/// field of that document is updated to `true`.
Future<void> call() async {
final documentCreatedEvent = DocumentCreatedEvent.fromCloudEvent(
firestore: firestore,
event: event,
);
final documentId = documentCreatedEvent.id;
final submittedByUserId =
documentCreatedEvent.value.data['submittedByUserId'] as String?;
if (submittedByUserId != null) {
await firestore
.collection('submissions')
.doc(documentId)
.update({'isVerified': true});
context.logger.debug(
'submission $documentId submitted by $submittedByUserId is verified',
);
} else {
context.logger.debug(
'''submission $documentId is not verified because submittedByUserId is null''',
);
}
}
}
Note that the implementation of DocumentCreatedEvent, which allows extracting the document ID and other information from the event of type CloudEvent as shown below, is a custom one.
Please check the sample repository for the specific content.
final documentCreatedEvent = DocumentCreatedEvent.fromCloudEvent(
firestore: firestore,
event: event,
);
final documentId = documentCreatedEvent.id;
In the Node.js SDK, simply writing it like the following allows you to immediately use the created document data (snapshot) and other metadata (context) within the function.
Since there is no package equivalent to npm's firebase-functions in Dart, I am thinking about releasing a package with a similar mechanism in the near future.
import * as functions from 'firebase-functions'
export const onCreateSubmission = functions
.region(`asia-northeast1`)
.firestore.document(`/submissions/{submissionId}`)
.onCreate(async (snapshot, context) => {
/** Omitted */
})
Conclusion
I have undertaken a somewhat challenging initiative titled "Running Firestore Trigger Functions in Dart using GCP."
The background that makes such an initiative possible is that Dart can now run on Cloud Run, Eventarc has become generally available in recent years, and packages like dart_firebase_admin and functions_framework have emerged.
Personally, I am very excited that the vision of "I wish I could write Firebase server-side code in Dart" is becoming a reality, something I've hoped for since I started developing apps with Flutter and Firebase 4–5 years ago.
Additionally, I am looking forward to it as I have found topics for future hobbyist OSS development, such as the fact that there is still no Dart package equivalent to npm's firebase-functions.
I intend to use this as an opportunity to continue writing server-side code in Dart and to explore packages related to server-side development with Dart.
I hope this serves as a helpful reference for those interested in similar endeavors.
Discussion