npm package: @simpleclub/firestore-decorators
Description: Decorators to help converting Firestore documents to classes.
Author: simpleclub
Latest version: 0.0.10

# Firestore decorators for Typescript

Firestore decorators is an provides a convenient method to generating 
[Firestore](https://firebase.google.com/docs/firestore) converters automagically.
It can be used with Typescript.

**This library currently only supports the [admin Firebase SDK](https://github.com/firebase/firebase-admin-node).**

It is inspired by the [Firebase Firestorm library](https://github.com/lmcq/firebase-firestorm/blob/master/README.md), 
but less opinionated how you should use the model.
It only parses models from Firestore documents to models and nothing more.

## Contents

-   [Requirements](#requirements)

-   [Installation](#installation)

-   [Usage](#usage)

    -   [Getting Started](#getting-started)
    -   [Custom Data Types](#custom-data-types)
    -   [Initialization Options](#initialization-options)

-   [Important Gotcha's](#important-gotchas)

-   [Limitations](#limitations)

-   [Development](#development)

    -   [Setup](#setup)
    -   [Testing](#testing)

-   [Contributing](#contributing)

-   [License](#license)

## Requirements

Firestore decorators relies on using Typescript's 
[experimental decorators](https://www.typescriptlang.org/docs/handbook/decorators.html)
for defining your models. Please ensure you have the following in your `tsconfig.json`
(ES5 is minimum target):

````json
{
  "compilerOptions": {
    "target": "ES5",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}
````

## Installation

For npm:
```bash
$ npm install firebase-decorators
```

For yarn:
```bash
$ yarn add firebase-decorators
```

## Usage

### Getting Started

Learn how to set up and us Firestore decorators to auto-generate Firestore converters.

#### 1. Initialize decorators

Call `decorators.initialize(firestore, options?)` as soon as you initialize
your firestore app. See [initialization options](###-initalization-options)
for more information about initializing Firestore decorators.

````typescript
import * as decorators from 'firestore-decorators';
...
const firestore = firebase.initializeApp(...).firestore();
decorators.initialize(firestore, /* options */);
...
````

#### 2. Defining collection models

Here we have a class representing a `posts` collection. Entity classes are
typically non-pluralized as they represent a single document from that
collection. To define a collection you must:

-   Extend from the `Entity` class.
-   Annotate your class with `@collection(opts: ICollectionConfig)`.
-   Declare a series of fields, annotated with `@field(opts: IFieldConfig)`.

```typescript
import { Entity, collection, field } from 'firestore-decorators';

@collection({
  path: 'posts',
})
export default class Post extends Entity {
  @field({ name: 'title' })
  title!: string;

  @field({ name: 'content' })
  content!: string;
}
```

#### 3. Defining subcollections

> Each of your models, whether they represent a root collection or
> subcollection must extend from the `Entity` class provided.

Now we want documents in the `posts` collection to have a subcollection 
of `comments`. First, we need to create a class for the comments. We also annotate the class with `@collection`, ]
but we use placeholders for the post ID.

```typescript
import { Entity, collection, field } from 'firestore-decorators';

@collection({
  path: 'posts/{post}/comments',
})
export default class Comment extends Entity {
  @field({ name: 'content' })
  content!: string;

  @field({ name: 'by' })
  by!: string;
}
```

#### 4. Defining document references

Finally, we want documents in the `posts` collection to reference an author in
an `authors` collection (another root collection). First, we define the `Author` entity:

```typescript
import { Entity, collection, field } from 'firestore-decorators';

@collection({
  path: 'authors',
})
export default class Author extends Entity {
  @field({ name: 'name' })
  name!: string;
}
```

Then we can add an `Author` reference to the `Post` entity using the `@documentRef(opts: IDocumentRefConfig)` decorator:

```typescript
import {firestore} from 'firebase-admin';
import { Entity, collection, documentRef } from 'firestore-decorators';
import Author from './Author';

@collection({
  path: 'posts',
})
export default class Post extends Entity {
  @documentRef({
    name: 'author',
    entity: Author
  })
  author!: firestore.DocumentReference<Author>;
  ...
}
```

### Custom Data Types

#### Arrays

Firestore documents can contain arrays of strings, numbers, objects,
etc. Defining arrays in Firestore decorators is as simple as assigning properties
as array types in your `Entity` files. For example:

```typescript
class Example extends Entity {
  @field({ name: 'example_property_1' })
  property1!: string[];

  @field({ name: 'example_property_2' })
  property2!: firestore.DocumentReference<AnotherEntity>[];
}
```

#### Nested Data

Firestore documents can contains nested objects (or maps). For a nested
object, you need to create a new class to represent that object, and add
a property with that class in your `Entity`, wrapped with the `@map` decorator.

```typescript
class Example extends Entity {
  @map({ name: 'nested_object' })
  nestedObject!: Nested;
}

class Nested {
  @field({ name: 'nested_property' })
  nestedProperty!: string;
}
```

And then to use this entity:

```typescript
const nested = new Nested();
nested.nestedProperty = 'test';
const example = new Example();
example.nestedObject = nested;
```

**Important**: If your is nested data is an array you must provide the 'entity'
option in the configuration.

```typescript
class Nested {
  @map({ name: 'nested_array', entity: Nested })
  nestedObject: Nested[];
}
```

#### Geopoints

Geopoints store locational data and can be used as fields.

```typescript
class Example extends Entity {
  @geoPoint({
    name: 'geopoint_property',
  })
  geopoint!: firestore.GeoPoint;
} 
```

And then to assign a GeoPoint:

```typescript
const example = new Example();
example.geopoint = new firestore.Geopoint(latitude, longitude);
```

#### Timestamps

You can represent date & time data in your `Entity` files.

```typescript
class Example extends Entity {
  @timestamp({
    name: 'timestamp_property',
  })
  timestamp!: firestore.Timestamp;
}
```

### Initialization Options

`Firestore decorators.intialize({ ...opts : IFireormConfig })` can be called
with the following options:

| Option            | Description                                                                                                                                                                                                               | Type                       |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- |
| `fieldConversion` | Providing this option will convert `Entity` propertity names into firestore collection names so you don't need to provide the `name` option in `@field()` decorators. To view available values please check out the docs. | `enum FieldConversionType` |

## Development

### Setup

1.  Clone the repo.
2.  Install dependencies.

```bash
cd firestore-decorators 
yarn install
```

### Testing

The testing script looks for `*.spec.ts` files in the `src`
and `test` directory.

```bash
yarn test
```

## Contributing

### Found a bug?

Please report any bugs you have found submitting an issue to our Github
repository, after ensuring the issue doesn't already exist. Alternatively,
you can make a pull request with a fix.

### Pull Requests

If you would like to help add or a feature or fix a bug, you can do so
by making a pull request. The project uses
[Conventional Commits](https://www.conventionalcommits.org), so please
make sure you follow the spec when making PRs. You must also
include relevant tests.

## License

[INSERT LICENSE](license.md)

## Contributing

If you wish to contribute a change in this repo,
please review our [contribution guide](https://github.com/simpleclub/firestore-decorators/blob/master/CONTRIBUTING.md),
and send a [pull request](https://github.com/simpleclub/firestore-decorators/pulls).