Simple .NET Core and Cloud Firestore setup
Tags: dotnet, firebase, firestore, programming
Published: 2021-12-03 21:28:19

> ... Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud. Like Firebase Realtime Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity...

Sounds cool, so how do I set it up with .Net Core (dotnet)?
It's very simple. 

I won't go into detail on how to setup firebase using console, there are plenty of tutorials out there. What we will need is the [service account json file](https://firebase.google.com/docs/admin/setup#initialize-sdk).

Let's start with the important part and create `FirebaseProvider` for generic use.

## Firebase Provider

Ah yeah and make sure you have this nuget package installed:

```xml
<PackageReference Include="Google.Cloud.Firestore" Version="2.4.0" />
```

```csharp
public class FirestoreProvider
{
    private readonly FirestoreDb _fireStoreDb = null!;

    public FirestoreProvider(FirestoreDb fireStoreDb)
    {
        _fireStoreDb = fireStoreDb;
    }

    public async Task AddOrUpdate<T>(T entity, CancellationToken ct) where T : IFirebaseEntity
    {
        var document = _fireStoreDb.Collection(typeof(T).Name).Document(entity.Id);
        await document.SetAsync(entity, cancellationToken: ct);
    }

    public async Task<T> Get<T>(string id, CancellationToken ct) where T : IFirebaseEntity
    {
        var document = _fireStoreDb.Collection(typeof(T).Name).Document(id);
        var snapshot = await document.GetSnapshotAsync(ct);
        return snapshot.ConvertTo<T>();
    }

    public async Task<IReadOnlyCollection<T>> GetAll<T>(CancellationToken ct) where T : IFirebaseEntity
    {
        var collection = _fireStoreDb.Collection(typeof(T).Name);
        var snapshot = await collection.GetSnapshotAsync(ct);
        return snapshot.Documents.Select(x => x.ConvertTo<T>()).ToList();
    }

    public async Task<IReadOnlyCollection<T>> WhereEqualTo<T>(string fieldPath, object value, CancellationToken ct) where T : IFirebaseEntity
    {
        return await GetList<T>(_fireStoreDb.Collection(typeof(T).Name).WhereEqualTo(fieldPath, value), ct);
    }
    
    // just add here any method you need here WhereGreaterThan, WhereIn etc ...

    private static async Task<IReadOnlyCollection<T>> GetList<T>(Query query, CancellationToken ct) where T : IFirebaseEntity
    {
        var snapshot = await query.GetSnapshotAsync(ct);
        return snapshot.Documents.Select(x => x.ConvertTo<T>()).ToList();
    }
}
```

## Usage

Inject it to your whatever it's going to use it.

```csharp
private readonly FirestoreProvider _firestoreProvider;

public SampleEndpoint(FirestoreProvider firestoreProvider)
{
    _firestoreProvider = firestoreProvider;
}
```

... and here is how you would actually use it:

```csharp
// Add or Update

var user = new User("Artur");
await _firestoreProvider.AddOrUpdate(user, cancellationToken);  

// Get by Id

var user = await _firestoreProvider.Get<User>("306f19a85136414b868ff7ba56c3f0a8", cancellationToken);

// Get all (use it wisely*)

var allUsers = await _firestoreProvider.GetAll<User>(cancellationToken);

// Query by first name (for example)

var users = await _firestoreProvider.WhereEqualTo<User>(nameof(Api.Data.User.FirstName), "Artur", ct);

```

## Data model

Now the boooooring part. 

Our simplified data model:

```csharp

[FirestoreData]
public class User : IFirebaseEntity
{
    [FirestoreProperty]
    public string Id { get; set; }

    [FirestoreProperty]
    public string FirstName { get; set; }

    public User()
    {
    }

    public User(string firstName)
    {
        Id = Guid.NewGuid().ToString("N");
        FirstName = firstName;
    }
}
```

The interfaces that forces to create Id which would be used as document id. We will use string version of `Guid` for that purpose.

```csharp
public interface IFirebaseEntity
{
    public string Id { get; set; }
}
```

## Provider registration

Register our provider in `Startup.cs` within `ConfigureServices`.

```csharp
services.AddSingleton(_ => new FirestoreProvider(
    new FirestoreDbBuilder 
    { 
        ProjectId = firebaseSettings.ProjectId, 
        JsonCredentials = firebaseJson // <-- service account json file
    }.Build()
));

```


## Firebase service account json

If you want a quick and dirty way of loading one without json files floating around your solution you can just hard code them into a class: 

```csharp
public class FirebaseSettings
{
    [JsonPropertyName("project_id")]
    public string ProjectId => "that-rug-really-tied-the-room-together-72daa";

    [JsonPropertyName("private_key_id")]
    public string PrivateKeyId => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    // ... and so on
}

```

In `Startup.cs` within `ConfigureServices`.

```csharp
var firebaseJson = JsonSerializer.Serialize(new FirebaseSettings());

```

... and you are done!


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ey29mq27y1ln1dt2836x.png)
 

If you want to have plural names for your collections just modify `FirestoreProvider` so that collection gets `s`:

```csharp
_fireStoreDb.Collection($"{typeof(T).Name}s")

```

Thank you for reading!

If you enjoyed this post and want to stay updated, follow me on [Twitter](https://twitter.com/KedziorArtur) for the latest updates and check out my projects on [GitHub](https://github.com/kedzior-io).

May the code be with you!