This library allows a user (or service account) to authenticate with Firestore and edit their Firestore database within a Google Apps Script.
Read how this project was started here.
As of v27, this project has been updated to use the GAS V8 runtime with Typescript! This introduces a number of breaking changes. Scripts utilizing the old Rhino runtime must use v26.
In the Google online script editor, select the Resources
menu item and choose Libraries...
. In the "Add a library" input box, enter 1VUSl4b1r1eoNcRWotZM3e87ygkxvXltOgyDZhixqncz9lQ3MjfT1iKFw
and click "Add." Choose the most recent version number.
The easiest way to use this library is to create a Google Service Account for your application and give it read/write access to your datastore. Giving a service account access to your datastore is like giving access to a user's account, but this account is strictly used by your script, not by a person.
If you don't already have a Firestore project you want to use, create one at the Firebase admin console.
To make a service account,
- Open the Google Service Accounts page by clicking here.
- Select your Firestore project, and then click "Create Service Account."
- For your service account's role, choose
Datastore > Cloud Datastore Owner
. - Check the "Furnish a new private key" box and select JSON as your key type.
- When you press "Create," your browser will download a
.json
file with your private key (private_key
), service account email (client_email
), and project ID (project_id
). Copy these values into your Google Apps Script — you'll need them to authenticate with Firestore. - [Bonus] It is considered best practice to make use of the Properties Service to store this sensitive information.
Now, with your service account client email address email
, private key key
, project ID projectId
, we will authenticate with Firestore to get our Firestore
object. To do this, get the Firestore
object from the library:
const firestore = FirestoreApp.getFirestore(email, key, projectId);
Here's a quick template to get you started (by replacing email
and key
with your values):
const email = '[email protected]';
const key = '-----BEGIN PRIVATE KEY-----\nPrivateKeyLine1\nPrivateKeyLine2\nPrivateKeyLineN\n-----END PRIVATE KEY-----';
const projectId = 'projectname-12345'
const firestore = FirestoreApp.getFirestore(email, key, projectId);
Alternatively, using Properties Service once data is already stored in the service with "client_email", "private_key", and "project_id" property names:
const props = PropertiesService.getUserProperties(); // Or .getScriptProperties() if stored in Script Properties
const [email, key, projectId] = [props.getProperty('client_email'), props.getProperty('private_key'), props.getProperty('project_id')];
const firestore = FirestoreApp.getFirestore(email, key, projectId);
Using this Firestore instance, we will create a Firestore document with a field name
with value test!
. Let's encode this as a JSON object:
const data = {
"name": "test!"
}
We can choose to create a document in collection called "FirstCollection" without a name (Firestore will generate one):
firestore.createDocument("FirstCollection", data);
Alternatively, we can create the document in the "FirstCollection" collection called "FirstDocument":
firestore.createDocument("FirstCollection/FirstDocument", data);
To update (overwrite) the document at this location, we can use the updateDocument
function:
firestore.updateDocument("FirstCollection/FirstDocument", data);
To update only specific fields of a document at this location, we can set the mask
parameter to true
:
firestore.updateDocument("FirstCollection/FirstDocument", data, true);
Or alternatiavely, we can set the mask
parameter to an array of field names:
firestore.updateDocument("FirstCollection/FirstDocument", data, ["field1", "field2", "fieldN"]);
this is useful for this:
If the document exists on the server and has fields not referenced in the mask, they are left unchanged. Fields referenced in the mask, but not present in the input document (the
data
in our example), are deleted from the document on the server.
To delete a document at this location, we can use the deleteDocument
function:
firestore.deleteDocument("FirstCollection/FirstDocument");
Note: This cannot handle deleting collections or subcollections, only individual documents.
You can retrieve documents by calling the getDocument
function:
const documentWithMetadata = firestore.getDocument("FirstCollection/FirstDocument");
You can also retrieve all documents within a collection by using the getDocuments
function:
const allDocuments = firestore.getDocuments("FirstCollection");
You can also get specific documents by providing an array of document names
const someDocuments = firestore.getDocuments("FirstCollection", ["Doc1", "Doc2", "Doc3"]);
You can access various properties of documents from Firestore:
const doc = firestore.getDocument("My Collection/My Document");
const originalData = doc.obj // Original database object (your stored data)
const readTime = doc.read // Date Object of the Read time from database
const updateTime = doc.updated // Date Object of the Updated time from database
const createdTime = doc.created // Date Object of the Created time from database
const name = doc.name // Full document path (projects/projName/databases/(default)/documents/My Collection/My Document)
const path = doc.path // Local document path (My Collection/My Document)
If more specific queries need to be performed, you can use the query
function followed by an .Execute()
invocation to get that data:
const allDocumentsWithTest = firestore.query("FirstCollection").Where("name", "==", "Test!").Execute();
The Where
function can take other operators too: ==
, <
, <=
, >
, >=
, contains
, contains_any
, in
.
Queries looking for null
values can also be given:
const allDocumentsNullNames = firestore.query("FirstCollection").Where("name", null).Execute();
Query results can be ordered:
const allDocumentsNameAsc = firestore.query("FirstCollection").OrderBy("name").Execute();
const allDocumentsNameDesc = firestore.query("FirstCollection").OrderBy("name", "desc").Execute();
To limit, offset, or just select a range of results:
const documents2_3_4_5 = firestore.query("FirstCollection").Limit(4).Offset(2).Execute();
const documents3_4_5_6 = firestore.query("FirstCollection").Range(3, 7).Execute();
See other library methods and details in the wiki.
-
I'm getting the following error:
Missing ; before statement. at [unknown function](Auth:12)
This is because this library has been updated to utilize the new V8 Engine, and classes are not supported in the Rhino Engine. You can either:
- Migrate your script to use V8, or
- Use the last Rhino version of this library (v26).
- v27: Library rewritten with Typescript and Prettier.
- Query function names have been capitalized (
Select
,Where
,OrderBy
,Limit
,Offset
,Range
). - All functions return
Document
orDocument[]
types directly from Firebase. Usedocument.obj
to extract the raw object. - Undo breaking change from v23.
document.createTime
anddocument.updateTime
will remain as timestamped strings. Howeverdocument.created
,document.updated
, anddocument.read
are Date objects.
- Query function names have been capitalized (
- v23: When retrieving documents the createTime and updateTime document properties are JS Date objects and not Timestamp Strings.
- v16: Removed:
createDocumentWithId(documentId, path, fields)
Utilize
createDocument(path + '/' + documentId, fields)
instead to create a document with a specific ID.
Contributions are welcome — send a pull request! See here for more information on contributing.
After cloning this repository, you can push it to your own private copy of this Google Apps Script project to test it yourself. See here for directions on using clasp
to develop App Scripts locally.
Install all packages from package.json
with a bare npm install
.
If you want to view the source code directly on Google Apps Script, where you can make a copy for yourself to edit, click here.