createDoc method

Future<TurboResponse<DocumentReference<Object?>>> createDoc({
  1. required TurboWriteable writeable,
  2. String? id,
  3. WriteBatch? writeBatch,
  4. TurboTimestampType createTimeStampType = TurboTimestampType.createdAtAndUpdatedAt,
  5. TurboTimestampType updateTimeStampType = TurboTimestampType.updatedAt,
  6. bool merge = false,
  7. List<FieldPath>? mergeFields,
  8. String? collectionPathOverride,
  9. Transaction? transaction,
})

Creates or writes a document to Firestore.

This method provides a flexible way to create or update documents with various options for handling timestamps, batching, transactions, and merging.

Parameters:

  • writeable The data to write, must implement TurboWriteable
  • id Optional custom document ID (auto-generated if not provided)
  • writeBatch Optional batch to include this operation in
  • createTimeStampType Type of timestamp to add for new documents
  • updateTimeStampType Type of timestamp to add when merging
  • merge Whether to merge with existing document
  • mergeFields Specific fields to merge (if merge is true)
  • collectionPathOverride Optional different collection path
  • transaction Optional transaction to include this operation in

Returns a TurboResponse containing either:

  • Success with the DocumentReference of the created document
  • Failure with validation errors or operation errors

Features:

  • Automatic validation through TurboWriteable.validate
  • Timestamp management based on operation type
  • Support for batch operations
  • Support for transactions
  • Merge/upsert capabilities

Example:

final user = User(name: 'John');
final response = await api.createDoc(
  writeable: user,
  id: 'user-123',
  merge: true,
);
response.when(
  success: (ref) => print('Created user: ${ref.id}'),
  fail: (error) => print('Error: $error'),
);

See also:

Implementation

Future<TurboResponse<DocumentReference>> createDoc({
  required TurboWriteable writeable,
  String? id,
  WriteBatch? writeBatch,
  TurboTimestampType createTimeStampType =
      TurboTimestampType.createdAtAndUpdatedAt,
  TurboTimestampType updateTimeStampType = TurboTimestampType.updatedAt,
  bool merge = false,
  List<FieldPath>? mergeFields,
  String? collectionPathOverride,
  Transaction? transaction,
}) async {
  assert(
    _isCollectionGroup == (collectionPathOverride != null),
    'Firestore does not support finding a document by id when communicating with a collection group, '
    'therefore, you must specify the collectionPathOverride containing all parent collection and document ids '
    'in order to make this method work.',
  );
  try {
    _log.debug(
        message: 'Checking if writeable is valid..', sensitiveData: null);
    final TurboResponse<DocumentReference>? invalidResponse =
        writeable.validate();
    if (invalidResponse != null && invalidResponse.isFail) {
      _log.warning(
        message: 'TurboWriteable was invalid!',
        sensitiveData: null,
      );
      return invalidResponse;
    }
    _log.info(message: 'TurboWriteable is valid!', sensitiveData: null);
    _log.debug(
      message: 'Creating document..',
      sensitiveData: SensitiveData(
        path: collectionPathOverride ?? _collectionPath(),
        id: id,
        isBatch: writeBatch != null,
        createTimeStampType: createTimeStampType,
        updateTimeStampType: updateTimeStampType,
        isMerge: merge,
        mergeFields: mergeFields,
        isTransaction: transaction != null,
      ),
    );
    if (writeBatch != null) {
      _log.debug(
          message: 'WriteBatch was not null! Creating with batch..',
          sensitiveData: null);
      final lastBatchResponse = await createDocInBatch(
        writeable: writeable,
        id: id,
        writeBatch: writeBatch,
        createTimeStampType: createTimeStampType,
        updateTimeStampType: updateTimeStampType,
        collectionPathOverride: collectionPathOverride,
        merge: merge,
        mergeFields: mergeFields,
      );
      _log.debug(
          message: 'Checking if batchCreate was successful..',
          sensitiveData: null);
      return _handleBatchOperation(lastBatchResponse);
    } else {
      _log.debug(
          message: 'WriteBatch was null! Creating without batch..',
          sensitiveData: null);
      final documentReference = id != null
          ? getDocRefById(
              id: id,
              collectionPathOverride: collectionPathOverride,
            )
          : _firebaseFirestore
              .collection(collectionPathOverride ?? _collectionPath())
              .doc();
      _log.debug(
        message: 'Creating JSON..',
        sensitiveData: null,
      );
      final writeableAsJson = (merge || mergeFields != null) &&
              (await documentReference.get(_getOptions)).exists
          ? updateTimeStampType.add(
              writeable.toJson(),
              updatedAtFieldName: _updatedAtFieldName,
              createdAtFieldName: _createdAtFieldName,
            )
          : createTimeStampType.add(
              writeable.toJson(),
              createdAtFieldName: _createdAtFieldName,
              updatedAtFieldName: _updatedAtFieldName,
            );
      var setOptions = SetOptions(
        merge: mergeFields == null ? merge : null,
        mergeFields: mergeFields,
      );
      if (transaction == null) {
        _log.debug(
          message: 'Setting data with documentReference.set..',
          sensitiveData: SensitiveData(
            path: collectionPathOverride ?? _collectionPath(),
            id: documentReference.id,
            data: writeableAsJson,
          ),
        );
        await documentReference.set(
          writeableAsJson,
          setOptions,
        );
      } else {
        _log.debug(
          message: 'Setting data with transaction.set..',
          sensitiveData: SensitiveData(
            path: collectionPathOverride ?? _collectionPath(),
            id: documentReference.id,
            data: writeableAsJson,
          ),
        );
        transaction.set(
          getDocRefById(id: documentReference.id),
          writeableAsJson,
          setOptions,
        );
      }
      _log.info(
        message: 'Setting data done!',
        sensitiveData: null,
      );
      return TurboResponse.success(result: documentReference);
    }
  } catch (error, stackTrace) {
    if (transaction != null) {
      // Wrap and rethrow for transactions
      throw TurboFirestoreException.fromFirestoreException(
        error,
        stackTrace,
        path: collectionPathOverride ?? _collectionPath(),
        query: 'createDoc(id: $id, merge: $merge)',
      );
    }

    _log.error(
      message: 'Unable to create document',
      sensitiveData: SensitiveData(
        path: collectionPathOverride ?? _collectionPath(),
        id: id,
        isBatch: writeBatch != null,
        createTimeStampType: createTimeStampType,
        updateTimeStampType: updateTimeStampType,
        isMerge: merge,
        mergeFields: mergeFields,
        isTransaction: false,
      ),
      error: error,
      stackTrace: stackTrace,
    );

    // Convert to TurboFirestoreException and wrap in TurboResponse
    final exception = TurboFirestoreException.fromFirestoreException(
      error,
      stackTrace,
      path: collectionPathOverride ?? _collectionPath(),
      query: 'createDoc(id: $id, merge: $merge)',
    );

    return TurboResponse.fail(error: exception);
  }
}