All Projects → mrbandler → Fsfirestore

mrbandler / Fsfirestore

Licence: mit
Functional F# library to access Firestore database hosted on Google Cloud Platform (GCP) or Firebase.

Programming Languages

fsharp
127 projects

Projects that are alternatives of or similar to Fsfirestore

Firegraph
GraphQL Superpowers for Google Cloud Firestore
Stars: ✭ 80 (+263.64%)
Mutual labels:  google, firebase, firestore
React Firebase Starter
Boilerplate (seed) project for creating web apps with React.js, GraphQL.js and Relay
Stars: ✭ 4,366 (+19745.45%)
Mutual labels:  gcp, firebase, firestore
Combinefirebase
Combine wrapper on Google's iOS Firebase library.
Stars: ✭ 126 (+472.73%)
Mutual labels:  google, firebase, firestore
Firebasecrud
Rich UI and animation flutter app backed by firebase
Stars: ✭ 121 (+450%)
Mutual labels:  firebase, firestore, firebase-database
Ccat
Cloud Container Attack Tool (CCAT) is a tool for testing security of container environments.
Stars: ✭ 300 (+1263.64%)
Mutual labels:  cloud, gcp, google
Vuex Easy Firestore
Easy coupling of firestore and a vuex module. 2-way sync with 0 boilerplate!
Stars: ✭ 224 (+918.18%)
Mutual labels:  firebase, firestore, store
Angularmaterialfirebase
🔥 Full stack starter app with Angular 8, Material Design and Firebase (+ demo)
Stars: ✭ 229 (+940.91%)
Mutual labels:  google, firebase, firebase-database
Angular 4 Material Pos
POS written in Angular 4 with Angular Material UI
Stars: ✭ 54 (+145.45%)
Mutual labels:  firebase, firestore, firebase-database
Infracost
Cloud cost estimates for Terraform in pull requests💰📉 Love your cloud bill!
Stars: ✭ 4,505 (+20377.27%)
Mutual labels:  cloud, gcp, google
Cloudprober
An active monitoring software to detect failures before your customers do.
Stars: ✭ 1,269 (+5668.18%)
Mutual labels:  cloud, gcp, google
Firebase Php
Unofficial Firebase Admin SDK for PHP
Stars: ✭ 1,657 (+7431.82%)
Mutual labels:  firebase, firestore, firebase-database
Sapphiredb
SapphireDb Server, a self-hosted, easy to use realtime database for Asp.Net Core and EF Core
Stars: ✭ 326 (+1381.82%)
Mutual labels:  firebase, firestore, core
Gatsby Theme Firebase
🔥 A Gatsby Theme for adding Firebase to your application.
Stars: ✭ 96 (+336.36%)
Mutual labels:  firebase, firestore, firebase-database
Nativescript Plugin Firebase
🔥 NativeScript plugin for Firebase
Stars: ✭ 990 (+4400%)
Mutual labels:  google, firebase, firestore
The Road To React With Firebase
📓The Road to React with Firebase: Your journey to build business applications with React and Firebase.
Stars: ✭ 82 (+272.73%)
Mutual labels:  firebase, firestore, firebase-database
Canner
⚡️[NOT MAINTAINED] Content Management Framework creates custom CMS fast and easy. Support data sources such as Firebase/Firestore, GraphQL and Restful APIs.
Stars: ✭ 2,472 (+11136.36%)
Mutual labels:  google, firebase, firestore
Firextensions
[DEPRECATED] 🔥 Unofficial Kotlin Extensions for the Firebase Android SDK.
Stars: ✭ 30 (+36.36%)
Mutual labels:  firebase, firestore, firebase-database
Firetable
Excel/Google Sheets like UI for Firebase/Firestore. No more admin portals!
Stars: ✭ 1,115 (+4968.18%)
Mutual labels:  gcp, firebase, firestore
Fake Gcs Server
Google Cloud Storage emulator & testing library.
Stars: ✭ 316 (+1336.36%)
Mutual labels:  cloud, gcp, google
Gifee
Google's Infrastructure for Everyone Else
Stars: ✭ 370 (+1581.82%)
Mutual labels:  cloud, gcp, google
FsFirestore Icon Banner

FsFirestore

Action status NuGet Badge

Donate with Bitcoin Donate with Litecoin Donate with Ethereum Donate

Functional F# library to access Firestore database hosted on Google Cloud Platform (GCP) or Firebase.

Table Of Content

  1. Usage ⌨️
  2. Bugs and Features 🐞💡
  3. Buy me a coffee
  4. License 📃

1. Usage

Connect to Firestore

To use any of the Firestore features you have to initialize the connection via a Service Account JSON (either for Firebase or GCP).

There are four ways to connect to your Firestore instance.

1. Service Account JSON Path

open FsFirestore.Firestore

let connected = connectToFirestore "./path/to/your/service_account.json"

The connectToFirestore function returns a boolean, to indicate whether the connection could be established.

2. Project ID

Set the path to your service account JSON to the environment variable GOOGLE_APPLICATION_CREDENTIALS and use your project ID to connect.

open FsFirestore.Firestore

connectToFirestoreProject "your_project_id"

3. Service Account JSON Path & DB Builder

Use a DB builder to connect, this lets you specify additional settings such as Custom Converters (example below).

open FsFirestore.Firestore
open Google.Cloud.Firestore

// Player ID.
type PlayerId (id: string) =
    let id = id
    member this.Id with get () = id

// Custom player ID converter.
type PlayerIdConverter () =
    interface IFirestoreConverter<PlayerId> with

    member this.ToFirestore value = value.Id :> obj

    member this.FromFirestore value =
        match value with
        | :? string as id -> PlayerId id
        | _ -> PlayerId ""

// Use custom type with custom converter.
[<FirestoreData>]
type Game () =
    inherit FirestoreDocument()

    [<FirestoreProperty>]
    member val PlayerA = PlayerId "0"

    [<FirestoreProperty>]
    member val PlayerB = PlayerId "1"

// connect :: Unit -> Boolean
let connect () =
    let converter = PlayerIdConverter ()
    let registry = ConverterRegistry ()
    registry.Add converter

    let builder = FirestoreDbBuilder ()
    builder.ConverterRegistry <- registry

    builder |> connectToFirestoreWithBuilder "./path/to/your/service_account.json"

4. DB Builder Only

Set the path to your service account JSON to the environment variable GOOGLE_APPLICATION_CREDENTIALS and use your project ID to connect.

open FsFirestore.Firestore
open Google.Cloud.Firestore

let builder = FirestoreDbBuilder ()
builder.ProjectId <- "your_project_id"

builder |> connectToFirestoreWithBuilderOnly

Create, Read, Update and Delete (CRUD)

After your successfully connected to your Firestore you can start manipulating data.

Model Classes

For better handling within the API we added generic retrieval functions, to simply retrieve your wanted model. Sadly the Google .NET API only let's us use classes, to compensate we created a base class that can be used to make your life easier. For a detailed description on data models for Firebase read this.

open Google.Cloud.Firestore
open FsFirestore.Types

[<FirestoreData>]
type Address() =
    inherit FirestoreDocument() // Base class that comes with FsFirestore

    [<FirestoreProperty>]
    member val Street = "Pennsylvania Avenue" with get, set

    [<FirestoreProperty>]
    member val HouseNo = 1600 with get, set

    [<FirestoreProperty>]
    member val City = "Washington" with get, set

    [<FirestoreProperty>]
    member val State = "DC" with get, set

// Because of the inheritance we now have some niffty features in this straight forward model class.
let address = new Address()

// We can retrieve all fields in a object list,
// which can later be used to query Firestore.
let fields = address.AllFields // => ["Pennsylvania Avenue"; 1600; "Washington"; "DC"]
let specificField = address.Fields("HouseNo", "City") // => [1600; "Washington"]

// We can ask the model which ID and collection it belongs to.
let docId = address.Id
let collectionId = address.CollectionId

Reading Documents

open Google.Cloud.Firestore
open FsFirestore.Firestore

// Let's read an address from Firestore and
// automatically convert it to our model.
let address = document<Address> "addresses" "POTUS-address"

// Again if your model inherits from "FirestoreDocument"
// you can use these features.
let docId = address.Id // => "POTUS-address""
let collectionId = address.CollectionId // => "addresses""

// --- or ---

// Let's just retrieve the document reference...
let addressRef = documentRef "addresses" "POTUS-address"
// And convert it to our model class manually.
let address = convertTo<Address> addressRef

// Again if your model inherits from "FirestoreDocument"
// you can use these features.
let docId = address.Id // => "POTUS-address""
let collectionId = address.CollectionId // => "addresses""

// --- or to check if a document exists ---

// Let's retrieve a document snapshot.
let addressSnap = documentSnapshot "addresses" "POTUS-address"

// Then you can check if a snapshot exists.
let exists = addressSnap.Exists // => true, false

Querying Documents

open Google.Cloud.Firestore
open FsFirestore.Firestore
open FsFirestore.Query

// To query a collection we first need to retrieve it from Firestore
let queryCollection = collection "addresses"

// Now we can chain conditions.
// Let's query all addresses in Pennsylvania Avenue, DC up to POTUS's one.
let addresses =
    queryCollection
    |> orderBy "HouseNo"
    |> whereEqualTo "State" "DC"
    |> whereEqualTo "Street" "Pennsylvania Avenue"
    |> whereGreaterThenOrEqualTo "HouseNo" 1
    |> whereLessThenOrEqualTo "HouseNo" 1600
    |> execQuery<Address>

Writing Documents

open Google.Cloud.Firestore
open FsFirestore.Firestore

// Let's create the model that we want to add to Firestore
let address = new Address()

// Now we can add the address to Firestore with a given
// collection name and ID.
let docRef = addDocument "addresses" (Some "POTUS-address") address

// -- or --

// We can also add the address and let the ID be generated automatically.
let docRef = addDocument "addresses" None address

Updating Documents

open FsFirestore.Firestore

// We can first the read the document we want to update
// from Firestore.
let address = document<Address> "addresses" "POTUS-address"

// Now let's move the presidents house number along one number.
address.HouseNo <- 1601

// And update the document within Firestore.
let docRef = updateDocument "addresses" "POTUS-address" address

Deleting Documents

open FsFirestore.Firestore

// To delete a document we simply need the collection ID
// and the document ID.
deleteDocument None "addresses" "POTUS-address"

// -- or --

// Additionally we can specify a precondition for the deletion process.
let timeStamp = Timestamp.FromDateTime(DateTime.Today)
let precondition = Precondition.LastUpdated(timeStamp)
deleteDocument precondition "addresses" "POTUS-address"

Transactions

Transactions are functions which take in a Transaction object to create, read, update, delete and query documents within the transaction scope.

Reading Documents in Transaction
open Google.Cloud.Firestore
open FsFirestore.Transaction

// Reading a document works just as expected only with the minor difference
// to use the transaction specific function from the Transaction module.
let transactionFunc (trans: Transaction) =
	documentInTrans<Address> trans "addresses" "POTUS-address"

// Now let's run the transaction.
// Notice that the transaction will return the return value from the actual transaction
// function.
let address = runTransaction transactionFunc

// -- or --

// You can specify the return values type, but F# will detected the type automatically
// most of the time.
let address = runTransaction<Address> transactionFunc
Writing Documents in Transaction
open Google.Cloud.Firestore
open FsFirestore.Transaction

// Let's create the model that we want to add to Firestore
let address = new Address()

// Now let's write a transaction to add an address to Firestore with a given
// collection name and ID.
let transactionFunc (trans: Transaction) =
	addDocumentInTrans trans "addresses" (Some "POTUS-address") address

// Let's run the transaction.
let docRef = runTransaction transactionFunc

// -- or --

// Of course, we can also add the address and let the ID be generated automatically.
let transactionFunc (trans: Transaction) =
	addDocumentInTrans trans "addresses" None address
Updating Documents in Transaction
open Google.Cloud.Firestore
open FsFirestore.Firestore

// Let's create our update transaction.
let transactionFunc (trans: Transaction) =
    // We can first read the document we want to update
    // from Firestore.
	let address = documentInTrans<Address> trans "addresses" "POTUS-address"

    // Now let's move the presidents house number along one number.
    address.HouseNo <- 1601

    // And update the document.
    updateDocumentInTrans trans "addresses" "POTUS-address" address

// Let's run the transaction.
let docRef = runTransaction transactionFunc
Deleting Documents in Transaction
open Google.Cloud.Firestore
open FsFirestore.Firestore

// Let's create our deletion transaction.
let transactionFunc (trans: Transaction) =
    // To delete a document we simply need the collection ID
    // and the document ID.
	deleteDocumentInTrans trans None "addresses" "POTUS-address"

// Let's run the transaction.
runTransaction transactionFunc

// -- or --

// As mentioned in the CRUD section we can also specify a precondition
// for the deletion process.
let transactionFunc (trans: Transaction) =
    let timeStamp = Timestamp.FromDateTime(DateTime.Today)
    let precondition = Precondition.LastUpdated(timeStamp)
    deleteDocumentInTrans trans precondition "addresses" "POTUS-address"

runTransaction transactionFunc

Listening for Changes

Firestore provides the ability to use a streaming API that let's you listen to changes made do specific documents or a complete set of documents managed by a query.

The listener functions will be called on document creation, deletion and update. You can even specify a listener functions for none existing documents, this requires a named document ID before hand.

Data Definition

For the listening examples we will use different model classes.

open Google.Cloud.Firestore
open FsFirestore.Types

// Stores scores per user, the username will be the document ID.
[<FirestoreData>]
type Score() =
	inherit FirestoreDocument() // Base class that comes with FsFirestore

	[<FirestoreProperty>]
	member val BestScore = 0 with get, set

	[<FirestoreProperty>]
	member val LastScore = 0 with get, set

// Stores a list of usernames that have high scores.
// A highscore is any score above 1000.
[<FirestoreData>]
type HighScores() =
	inherit FirestoreDocument() // Base class that comes with FsFirestore

	[<FirestoreProperty>]
	member val Usernames = [] with get, set

Mounting Listeners

open FsFirestore.Firestore
open FsFirestore.Listening

// To creater a listener it's as easy as writing a simple function,
// as it practically is just a function.
let callback (snap: DocumentSnapshot) =
	if snap.Exists = true then
		// The callback takes in a document snapshot, we can convert the snap
		// to our model.
		let score = convertSnapshotTo<Score> snap

		// Now we can use the listener to set the best score to the last score
		// if it was better then the current best score.
		if score.LastScore > score.BestScore then
			score.BestScore <- score.LastScore
			updateDocument score.CollectionId score.Ids score

// Now we can simple mount our created listener callback and in
// turn receive a listener object from Firestore
let listener = listenOnDocument "scores" "mrbandler" callback

// If we want to stop listening we just stop listening.
stopListening listener

Listening For Query Changes

open FsFirestore.Firestore
open FsFirestore.Listening

// Now we can use a listener to update a different document with the
// best high scores.
let callback (querySnap: QuerySnapshot) =
	// If the query changes the callback is called and we can retrieve the
	// updated query results.
	let scores = convertSnapshotsTo<Score> querySnap.Documents |> List.ofSeq

	// Now we can extract the usernames from the scores into an array.
	let usernames =
		scores
		|> List.map (fun score -> score.Id)
		|> Array.ofList

	// Let's update our highscores document with the new usernames.
	let highScores = document<HighScores> "highscores" "users"
	highScores.Usernames <- usernames

	updateDocument highScores.CollectionId highScores.Id highScores

// Now let's mount our created listener callback to a query.
// We only want highscores that are above 1000, to be neat we also order them.
let listener =
	collection "scores"
	|> whereGreaterThen "BestScore" 1000
	|> orderBy "BestScore"
	|> listenOnQuery callback

// If we want to stop listening we just stop listening.
stopListening listener

In the above example we a simply retrieving all documents from the query which can be a lot of data. There is a way to only work with the changes from the query.

open FsFirestore.Firestore
open FsFirestore.Listening

// Now we can use a listener to update a different document with the
// best high scores.
let callback (querySnap: QuerySnapshot) =
	// If the query changes the callback is called and we can retrieve the
	// updated query results.
	let scoreChanges = convertQueryChanges<Score> querySnap.Changes |> List.ofSeq

	// A document change contains a bit more data then a usual document
	let scoreChange = List.item 0
	let doc = scoreChange.document           // => Actuall converted document data
 	let changeType = scoreChange.changeType  // => Added (there also is Updated and Removed)
 	let newIndex = scoreChange.newIndex      // => New index (option) if moved in the query
	let oldIndex = scoreChange.oldIndex      // => Old index (option) if moved in the query

	// Now we can extract the usernames from the scores into an array.
	let usernames =
		scoreChanges
		|> List.filter (fun scoreChange -> scoreChange.changeType = DocumentChange.Type.Added)
		|> List.map (fun scoreChange -> scoreChange.document.Id)
		|> Array.ofList

	// Let's update our highscores document with the new usernames.
	let highScores = document<HighScores> "highscores" "users"
	Array.append highScores.Usernames usernames // Now instead of overwriting the all usernames we add to the array.
	updateDocument highScores.CollectionId highScores.Id highScores

// Now let's mount our created listener callback to a query.
// We only want highscores that are above 1000, to be neat we also order them.
let listener =
	collection "scores"
	|> whereGreaterThen "BestScore" 1000
	|> orderBy "BestScore"
	|> listenOnQuery callback

// If we want to stop listening we just stop listening.
stopListening listener

2. Bugs and Features

Please open a issue when you encounter any bugs 🐞 or have an idea for a new feature 💡.

3. Buy me a coffee

If you like you can buy me a coffee:

Support via PayPal

Donate with Bitcoin

Donate with Litecoin

Donate with Ethereum


4. License

MIT License

Copyright (c) 2018 fivefingergames

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].