This is a simple Firestore Admin Client implemented for Deno. It provides an easy-to-use interface for interacting with Firestore databases using the Google Cloud Firestore API.
- Authentication using Google Service Account
- Automatic token refresh
- CRUD operations on Firestore documents
- Support for querying collections
- Conversion between Firestore document format and JSON
- Deno installed on your system
- A Google Cloud project with Firestore enabled
- A service account JSON file with the necessary permissions
- Set the
FIREBASE_SERVICE_ACCOUNT
environment variable with the contents of your service account JSON file.
You can set this environment variable in a .env
file (do not forget to add the
--env
flag when running your script):
# .env
FIREBASE_SERVICE_ACCOUNT='{
"type": "service_account",
"project_id": "XXX",
"private_key_id": "XXX",
"private_key": "XXX",
"client_email": "[email protected]",
"client_id": "XXX",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/XXX.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}'
- Install
@koiztech/firestore-admin
as a dependency:
deno add jsr:@koiztech/firestore-admin
- Import
FirestoreAdminClient
in your Deno script and initialise it:
import { FirestoreAdminClient } from "@koiztech/firestore-admin";
const firestore = new FirestoreAdminClient();
firestore-admin
will take care of parsing the data to/from JSON when
fetching/updating documents.
If you work with Firestore Timestamp fields, read the
Using timestamps section.
If you need to query
documents by their ID, read the Using document IDs in a filter
section.
const document = await firestore.createDocument("my-collection", {
name: "John Doe",
});
// document = {
// _id: 'XXX',
// _path: 'my-collection/XXX',
// name: 'John Doe',
// }
Returns the created document with an additional _id
and _path
fields, which
are the document ID and path respectively.
const documents = await firestore.listDocumentsInCollection("my-collection");
// documents = ['document1', 'document2']
Returns an array of document IDs.
Note: The fetched documents will have an additional _id
field, which is the
document ID. getDocumentsInCollectionGroup
additionally returns a _path
property.
To simply fetch all documents in a collection, use getDocumentsInCollection
,
e.g.:
const collection = await firestore.getDocumentsInCollection("my-collection");
If you want to fetch documents from a sub collection (aka. collection group),
you can do so with getDocumentsInCollectionGroup
, e.g.:
// e.g. `my-sub-collection` is a sub collection of `my-collection/{someId}`
const collection = await firestore.getDocumentsInCollectionGroup(
"my-sub-collection",
);
Note: The fetched documents will have an additional _id
field, which is the
document ID.
You can use the same syntax for both
getDocumentsInCollection
andgetDocumentsInCollectionGroup
methods. The latter also returns a_path
property.
// Import the FirestoreOperator enum
import { FirestoreOperator } from "@koiztech/firestore-admin";
// e.g.
const documents = await firestore.getDocumentsInCollection("my-collection", {
where: {
filters: [
["name", FirestoreOperator.EQUAL, "John Doe"],
],
},
});
// e.g.
const document = await firestore.getDocument("my-collection", {
where: {
filters: [
["name", FirestoreOperator.IN, ["John Doe", "Max Mustermann"]], // example of an IN filter
],
},
});
// e.g. a more complex query
const documents = await firestore.getDocumentsInCollection("my-collection", {
where: {
filters: [
["name", FirestoreOperator.EQUAL, "Ivan Petrov"],
["height", FirestoreOperator.LESS_THAN, 200],
["address.city", FirestoreOperator.EQUAL, "Moscow"], // example of a nested field
[
"bornAt",
FirestoreOperator.GREATER_THAN,
new Date("1990-01-01T12:50:00.000Z"),
], // example of a timestamp filter
],
},
orderBy: [{ field: "createdAt", direction: "DESCENDING" }], // you can sort the results
limit: 3, // you can limit the number of results
});
const document = await firestore.getDocument("my-collection/my-document");
await firestore.updateDocument("my-collection/my-document", {
name: "John Doe",
});
// ...or with specific update fields
await firestore.updateDocument("my-collection/my-document", {
name: "John Doe",
age: 30, // this field will not be updated
address: {
city: "Dubai",
country: "United Arab Emirates", // this field will not be updated
},
}, ["name", "address.city"]); // you can use nested fields as well
Returns the updated document with an additional _id
and _path
fields.
If you need to use Firestore timestamps, simply use Date
objects, e.g.:
const now = new Date();
// ...and then use it in your document:
await firestore.createDocument("my-collection", {
createdAt: now,
});
The above Date
object will be converted to a Firestore timestamp
automatically.
When filtering results by timestamp, make sure to use Date
objects as well,
e.g.:
const documents = await firestore.getDocumentsInCollection("my-collection", {
where: {
filters: [[
"createdAt",
FirestoreOperator.GREATER_THAN,
new Date("2024-12-02"),
]],
},
});
In the query results, timestamp fields will be returned as an ISO Date string,
e.g. 2025-01-01T00:00:00.000Z
.
If you need to filter query results by document IDs in the
getDocumentsInCollection
method, you can do so with the documentId
field.
For example:
// Will return all documents in the collection with the document IDs `docId1` and `docId2`.
const documents = await firestore.getDocumentsInCollection("my-collection", {
where: {
filters: [["documentId", FirestoreOperator.IN, ["docId1", "docId2"]]],
},
});
// You can still combine it with other filters, e.g.:
const documents = await firestore.getDocumentsInCollection("my-collection", {
where: {
filters: [
["documentId", FirestoreOperator.IN, ["docId1", "docId2"]],
["isActive", FirestoreOperator.EQUAL, true],
],
},
});