Firestore decorators for Typescript
Firestore decorators is an provides a convenient method to generating Firestore converters automagically. It can be used with Typescript.
This library currently only supports the admin Firebase SDK.
It is inspired by the Firebase Firestorm library, but less opinionated how you should use the model. It only parses models from Firestore documents to models and nothing more.
Contents
Requirements
Firestore decorators relies on using Typescript's
experimental decorators
for defining your models. Please ensure you have the following in your tsconfig.json
(ES5 is minimum target):
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Installation
For npm:
$ npm install firebase-decorators
For yarn:
$ 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
for more information about initializing Firestore decorators.
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)
.
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.
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:
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:
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:
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.
class Example extends Entity {
@map({ name: 'nested_object' })
nestedObject!: Nested;
}
class Nested {
@field({ name: 'nested_property' })
nestedProperty!: string;
}
And then to use this entity:
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.
class Nested {
@map({ name: 'nested_array', entity: Nested })
nestedObject: Nested[];
}
Geopoints
Geopoints store locational data and can be used as fields.
class Example extends Entity {
@geoPoint({
name: 'geopoint_property',
})
geopoint!: firestore.GeoPoint;
}
And then to assign a GeoPoint:
const example = new Example();
example.geopoint = new firestore.Geopoint(latitude, longitude);
Timestamps
You can represent date & time data in your Entity
files.
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
- Clone the repo.
- Install dependencies.
cd firestore-decorators
yarn install
Testing
The testing script looks for *.spec.ts
files in the src
and test
directory.
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, so please make sure you follow the spec when making PRs. You must also include relevant tests.
License
Contributing
If you wish to contribute a change in this repo, please review our contribution guide, and send a pull request.