Apollo-Firesource

Apollo server datasource wrapping Firestore REST APIs.    [PRs welcome 😊]

Basic Usage

  • Set required environment variables:

    • FIRESOURCE_CREDENTIALS=<Path-to-service-account-json> // must have firestore access
  • Pass FireSource instance as option to ApolloServer constructor:

    const server = new ApolloServer({
      typeDefs,
      resolvers,
      dataSources: () => {
        return {
          firestore: new FireSource({
            projectId: <Firestore-project-id>
            // ... other options
          }),
        };
      },
    });
    
  • Access the data source from resolvers:

    Query: {
      document: async (parent, args, ctx) => {
        const { documentPath } = args;
        const { dataSources: { firestore } } = ctx;
    
        try {
          // get document by id from collection
          return firestore.documents().get({ documentPath });
        } catch (error) {
          /* handle error
           *
           * Ideally first handle Firestore error responses:
           *   error.extensions?.response - exception details
           *
           * Then FireSource Errors:
           *   error.extensions.code === <'BAD_USER_INPUT'|'UNAUTHENTICATED'>
           *   error.message - exception details
           */
        }
      },
    },
    

    API

    Constructor - firestore = new FireStore(config):

    • config (object):
      {
        projectId: string, // (required) Firestore project ID
        version: 'v1', // string (optional)
        resource: 'databases', // string: <'databases' | 'locations'> (optional)
        database: '(default)', // string (optional)
      }
      

    Documents - firestore.documents()

    • presets

      • documentBasePath = projects/<Firestore-project-id>/databases/<database>/documents Paths relative to documentBasePath (collectionPath/documentPath) must start with and not end with '/'   ex: '/users/bob'
    • methods (async):

      • batchGet(options) => batchGetResult

        Get multiple documents from database.

        • options (object)

          {
            documents: string[], // (required) Array of document paths relative to documentBasePath ('.../databases/<db>/documents')  ex '/users/mike1'
            fieldsToReturn: string[], // (optional) array of document fields to include in response
          
            // (optional)
            consistencySelector: {
              // Union field consistencySelector can be only one of the following:
              transaction: string,  // Reads documents in a transaction: A base64-encoded string.
              newTransaction: TransactionOptions, // https://cloud.google.com/firestore/docs/reference/rest/v1/TransactionOptions
              readTime: string
            }
          }
          
        • returns (object): BatchGetResult

          {
            documents: Array,
            documentCount:number,
            missing: Array,
            readTime: string,
            transaction: string
          }
          


      • beginTransaction(options) => object

        Start a new transaction.

        • options (object): optional

          // https://cloud.google.com/firestore/docs/reference/rest/v1/TransactionOptions
          {
            readOnly: {
              readTime: string // (optional): Timestamp format
            },
            readWrite: {
              retryTransaction: string // (optional)
            }
          
          }
          
        • returns (object):

          {
            transaction: string;
          }
          


      • create(options) => Document

        Insert new document into a collection.

        • options (object)

          {
            collectionPath: string, // (required) path to parent collection relative to documentBasePath
            docId: string   // (optional) custom document id
            fieldsToReturn: string[] // (optional) array of fields to include in response (mask)
            data: {
              name: string  // (optional) document resource name
              fields: {
                "fieldName": {
                  // where 'valueType' is one of https://cloud.google.com/firestore/docs/reference/rest/v1/Value
                  valueType: value
                }
              }
            }
          }
          
        • returns (object): Document

          {
            name: string
            fields: object,
            createTime: string,
            updateTime: string,
          }
          


      • commit(options) => CommitResponse

        Commits a transaction while optionally updating documents.

        • options (object)

          {
            transaction: string, // (required) A base64-encoded transaction id string.
          
            // writes is an array of objects
            writes: [
              {
                // (required)  The operation to execute
                operation: {
                  // Union field operation can be only one of the following:
                  delete: string,   // Path to document to delete relative to documentBasePath,  ex. '/users/joe'
                  transform: {
                    documentPath: string,  // (required) Path to document relative to documentBasePath
                    fieldTransforms: Array<fieldTransform> // (required) Array of field transforms to apply;
                  }
                  update: {
                    documentPath: string,  // (required) Path to document to update relative to documentBasePath
                    fields: {
                      fieldName: {
                        // where 'valueType' is one of https://cloud.google.com/firestore/docs/reference/rest/v1/Value
                        valueType: value
                      }
                    },
                  };
                }
          
                // (optional)
                currentDocument: {
                  // Union field currentDocument can be only one of the following:
                  exists: boolean,   // (optional) When set to true, the target document must exist. When set to false, the target document must not exist
                  updateTime: string // (optional) Timestamp format When set, the target document must exist and have been last updated at that time
                }
          
                // (optional) Can be set only when the operation is update. if not set, existing document is overitten
                updateOptions: {
                  // Union field updateOptions can be only one of the following:
                  updateAll: boolean,  // (optional) update all fields
                  fieldsToUpdate: string[], // array of fields to update
                }
              }
            ]
          }
          
        • fieldTransform (object)

          {
            fieldPath: string; // (required) Path of field to transform
            transformType: {
              // Union field transformType can be only one of the following:
              setToServerValue: 'SERVER_VALUE_UNSPECIFIED' | 'REQUEST_TIME'; // Sets the field to the given server value.
              increment: object; // Adds the given value to the field's current value.
              maximum: object; // Sets the field to the maximum of its current value and the given value.
              minimum: object; // Sets the field to the minimum of its current value and the given value.
              appendMissingElements: object; // Append the given elements in order if they are not already present in the current field value
              removeAllFromArray: object; //  Remove all of the given elements from the array in the field.
            }
          }
          
        • returns (object): CommitResponse

          {
            writeResults: Array; // https://cloud.google.com/firestore/docs/reference/rest/v1/WriteResult
            commitTime: string;
          }
          


      • delete(options) => object

        Delete document from collection.

        • options (object)

          {
            documentPath: string, // (required) path to document relative to documentBasePath
          
            // (optional)
            currentDocument: {
              exists: boolean,  // (optional) When set to true, the target document must exist. When set to false, the target document must not exist
              updateTime: string // (optional) Timestamp format: When set, the target document must exist and have been last updated at that time
            }
          }
          
        • returns (object):

          {
            deleted: boolean;
          }
          


      • get(options) => DocumentData | DocumentData[]

        Retrieve one or all documents from a collection.

        • options (object)
          {
            collectionPath: string,  // (optional) relative path to collection: if set and !documentPath ,get all documents in collection
            documentPath: string,    // (optional) relative path to document: if set and !collectionPath, get single document
            fieldsToReturn: string[]   // (optional) Array of fields to include in response (mask)
          }
          
        • returns (object): Document | Documents

          // Document
          {
            name: string
            fields: object,
            createTime: string,
            updateTime: string,
          }
          
          // Documents
          {
            documents: Document[];
            documentCount: number;
          }
          


      • list(options) => ListResult

        Get a list of documents

        • options (object)

          {
            collectionPath: string;    // (required) Full collection path relative to documentPath ex: '/users'
            fieldsToReturn: string[];  // (optional) array of fields to include in response (mask)
          
            // (optional)
            queryOptions: {
              pageSize: number,      // (optional) Maximum number of documents to return
              pageToken: string,     // (optional) The nextPageToken value returned from a previous List request, if any.
              showMissing: boolean,  // (optional)  Show missing documents. Requests with showMissing may not specify orderBy.
              orderBy: string,       // (optional) The order to sort results by. For example: priority desc, name.
          
              // (optional)
              consistencySelector: {
                // can be only one of the following:
                transaction: string,   // (optional) Reads documents in a transaction: A base64-encoded string
                readTime: string     // (optional) string (Timestamp format): Reads documents as they were at the given time
              }
            }
          }
          
        • returns (object): ListResult

          {
            documents: Array,
            documentCount:number,
            nextPageToken: string  // The next page token.
          }
          


      • listCollectionIds(options) => ListCollectionIdsResult

        List IDs of collections that are children of given document

        • options (object)

          {
            documentPath: string;   // (required) Full path of parent document with child collection ids to list ex: '/chatrooms/roomx'
            pageSize: number,       // (optional) Maximum number of ids to return
            pageToken: string,     // (optional) The nextPageToken value returned from a previous listCollectionIds request, if any.
          }
          
        • returns (object): ListCollectionIdsResult

          {
            collectionIds: string[],
            idCount: number,
            nextPageToken: string  // The next page token.
          }
          


      • rollBack(options) => RollbackResult

        Roll back a transaction.

        • options (object)

          {
            transaction: string; // (required) Transaction to rollback A base64-encoded string
          }
          
        • returns (object): RollbackResult

          {
            rolledBack: true;
          }
          


- `runQuery(options) => queryResult`

  Run a query against the database.

  - **options** (_object_)

    ```javascript
    {
      structuredQuery: object, // (required) https://cloud.google.com/firestore/docs/reference/rest/v1/StructuredQuery
      documentPath: string,  // (optional)  path relative to documentBasePath

      // (optional)
      consistencySelector: {
        // can be only one of the following:
        transaction: string, // Reads documents in a transaction: A base64-encoded string
        newTransaction: TransactionOptions, // https://cloud.google.com/firestore/docs/reference/rest/v1/TransactionOptions
        readTime: string
      }
    }
    ```

  - **returns** (object): QueryResult
    ```javascript
    {
      documents: Array,
      documentCount:number,
      readTime: string,
      skippedResults: number,
      transaction: string
    }
    ```

  <br />

- `update(options) => Document`

  Update document in collection by id.

  - **options** (_object_)

    ```javascript
    {
      documentPath: string,  // (required)  document path relative to documentBasePath
      fieldsToReturn: string[], // (optional) array of fields to include in response (mask)
      data: {
        name: string, // (optional) document resource name
        fields: {
          "fieldName": {
            // where 'valueType' is one of https://cloud.google.com/firestore/docs/reference/rest/v1/Value
            valueType: value
          }
        }
      },
      updateOptions: {
        updateAll: boolean, // (optional) update all fields
        fieldsToUpdate: string[], // (optional, required if !updateAll) array of fields to update

        // (optional)
        currentDocument: {
          exists: boolean, // (optional) When set to true, the target document must exist. When set to false, the target document must not exist
          updateTime: string // (optional) Timestamp format When set, the target document must exist and have been last updated at that time
        }
      }
    }
    ```

  - **returns** (_object_): The updated document

  <br />

API

Constructor - firestore = new FireStore(config):

  • config (object):
    {
      projectId: string, // (required) Firestore project ID
      version: 'v1', // string (optional)
      resource: 'databases', // string: <'databases' | 'locations'> (optional)
      database: '(default)', // string (optional)
    }
    

Documents - firestore.documents()

  • presets

    • documentBasePath = projects/<Firestore-project-id>/databases/<database>/documents Paths relative to documentBasePath (collectionPath/documentPath) must start with and not end with '/'   ex: '/users/bob'
  • methods (async):

    • batchGet(options) => batchGetResult

      Get multiple documents from database.

      • options (object)

        {
          documents: string[], // (required) Array of document paths relative to documentBasePath ('.../databases/<db>/documents')  ex '/users/mike1'
          fieldsToReturn: string[], // (optional) array of document fields to include in response
        
          // (optional)
          consistencySelector: {
            // Union field consistencySelector can be only one of the following:
            transaction: string,  // Reads documents in a transaction: A base64-encoded string.
            newTransaction: TransactionOptions, // https://cloud.google.com/firestore/docs/reference/rest/v1/TransactionOptions
            readTime: string
          }
        }
        
      • returns (object): BatchGetResult

        {
          documents: Array,
          documentCount:number,
          missing: Array,
          readTime: string,
          transaction: string
        }
        


    • beginTransaction(options) => object

      Start a new transaction.

      • options (object): optional

        // https://cloud.google.com/firestore/docs/reference/rest/v1/TransactionOptions
        {
          readOnly: {
            readTime: string // (optional): Timestamp format
          },
          readWrite: {
            retryTransaction: string // (optional)
          }
        
        }
        
      • returns (object):

        {
          transaction: string;
        }
        


    • create(options) => Document

      Insert new document into a collection.

      • options (object)

        {
          collectionPath: string, // (required) path to parent collection relative to documentBasePath
          docId: string   // (optional) custom document id
          fieldsToReturn: string[] // (optional) array of fields to include in response (mask)
          data: {
            name: string  // (optional) document resource name
            fields: {
              "fieldName": {
                // where 'valueType' is one of https://cloud.google.com/firestore/docs/reference/rest/v1/Value
                valueType: value
              }
            }
          }
        }
        
      • returns (object): Document

        {
          name: string
          fields: object,
          createTime: string,
          updateTime: string,
        }
        


    • commit(options) => CommitResponse

      Commits a transaction while optionally updating documents.

      • options (object)

        {
          transaction: string, // (required) A base64-encoded transaction id string.
        
          // writes is an array of objects
          writes: [
            {
              // (required)  The operation to execute
              operation: {
                // Union field operation can be only one of the following:
                delete: string,   // Path to document to delete relative to documentBasePath,  ex. '/users/joe'
                transform: {
                  documentPath: string,  // (required) Path to document relative to documentBasePath
                  fieldTransforms: Array<fieldTransform> // (required) Array of field transforms to apply;
                }
                update: {
                  documentPath: string,  // (required) Path to document to update relative to documentBasePath
                  fields: {
                    fieldName: {
                      // where 'valueType' is one of https://cloud.google.com/firestore/docs/reference/rest/v1/Value
                      valueType: value
                    }
                  },
                };
              }
        
              // (optional)
              currentDocument: {
                // Union field currentDocument can be only one of the following:
                exists: boolean,   // (optional) When set to true, the target document must exist. When set to false, the target document must not exist
                updateTime: string // (optional) Timestamp format When set, the target document must exist and have been last updated at that time
              }
        
              // (optional) Can be set only when the operation is update. if not set, existing document is overitten
              updateOptions: {
                // Union field updateOptions can be only one of the following:
                updateAll: boolean,  // (optional) update all fields
                fieldsToUpdate: string[], // array of fields to update
              }
            }
          ]
        }
        
      • fieldTransform (object)

        {
          fieldPath: string; // (required) Path of field to transform
          transformType: {
            // Union field transformType can be only one of the following:
            setToServerValue: 'SERVER_VALUE_UNSPECIFIED' | 'REQUEST_TIME'; // Sets the field to the given server value.
            increment: object; // Adds the given value to the field's current value.
            maximum: object; // Sets the field to the maximum of its current value and the given value.
            minimum: object; // Sets the field to the minimum of its current value and the given value.
            appendMissingElements: object; // Append the given elements in order if they are not already present in the current field value
            removeAllFromArray: object; //  Remove all of the given elements from the array in the field.
          }
        }
        
      • returns (object): CommitResponse

        {
          writeResults: Array; // https://cloud.google.com/firestore/docs/reference/rest/v1/WriteResult
          commitTime: string;
        }
        


    • delete(options) => object

      Delete document from collection.

      • options (object)

        {
          documentPath: string, // (required) path to document relative to documentBasePath
        
          // (optional)
          currentDocument: {
            exists: boolean,  // (optional) When set to true, the target document must exist. When set to false, the target document must not exist
            updateTime: string // (optional) Timestamp format: When set, the target document must exist and have been last updated at that time
          }
        }
        
      • returns (object):

        {
          deleted: boolean;
        }
        


    • get(options) => DocumentData | DocumentData[]

      Retrieve one or all documents from a collection.

      • options (object)
        {
          collectionPath: string,  // (optional) relative path to collection: if set and !documentPath ,get all documents in collection
          documentPath: string,    // (optional) relative path to document: if set and !collectionPath, get single document
          fieldsToReturn: string[]   // (optional) Array of fields to include in response (mask)
        }
        
      • returns (object): Document | Documents

        // Document
        {
          name: string
          fields: object,
          createTime: string,
          updateTime: string,
        }
        
        // Documents
        {
          documents: Document[];
          documentCount: number;
        }
        


    • list(options) => ListResult

      Get a list of documents

      • options (object)

        {
          collectionPath: string;    // (required) Full collection path relative to documentPath ex: '/users'
          fieldsToReturn: string[];  // (optional) array of fields to include in response (mask)
        
          // (optional)
          queryOptions: {
            pageSize: number,      // (optional) Maximum number of documents to return
            pageToken: string,     // (optional) The nextPageToken value returned from a previous List request, if any.
            showMissing: boolean,  // (optional)  Show missing documents. Requests with showMissing may not specify orderBy.
            orderBy: string,       // (optional) The order to sort results by. For example: priority desc, name.
        
            // (optional)
            consistencySelector: {
              // can be only one of the following:
              transaction: string,   // (optional) Reads documents in a transaction: A base64-encoded string
              readTime: string     // (optional) string (Timestamp format): Reads documents as they were at the given time
            }
          }
        }
        
      • returns (object): ListResult

        {
          documents: Array,
          documentCount:number,
          nextPageToken: string  // The next page token.
        }
        


    • listCollectionIds(options) => ListCollectionIdsResult

      List IDs of collections that are children of given document

      • options (object)

        {
          documentPath: string;   // (required) Full path of parent document with child collection ids to list ex: '/chatrooms/roomx'
          pageSize: number,       // (optional) Maximum number of ids to return
          pageToken: string,     // (optional) The nextPageToken value returned from a previous listCollectionIds request, if any.
        }
        
      • returns (object): ListCollectionIdsResult

        {
          collectionIds: string[],
          idCount: number,
          nextPageToken: string  // The next page token.
        }
        


    • rollBack(options) => RollbackResult

      Roll back a transaction.

      • options (object)

        {
          transaction: string; // (required) Transaction to rollback A base64-encoded string
        }
        
      • returns (object): RollbackResult

        {
          rolledBack: true;
        }