Repository Pattern implementation for Firebase Realtime Database
Getting Started
Installation
Usage
Built With
Contributing
Inspired by Fireorm, Dreepo exposes a Repository Pattern implementation for Firebase Realtime Database.
Alongside an abstract database access layer, repositories also support:
- aggregation pipelines, searches, and url queries using mango
- model validation using class-validator
-
Create or edit an
.npmrc
file with the following information:@flex-development:registry=https://npm.pkg.github.com/
-
Add project to
dependencies
yarn add @flex-development/dreepo # or npm i @flex-development/dreepo
Configuration
Database Connection
Modeling Entities
Repository Options
Creating a New Repository
Repository Cache
Repository Class API
Dreepo communicates with your Realtime Database using the Firebase Database REST API. Generated by service accounts, Google OAuth2 access tokens are used to authenticate requests.
-
Navigate to the Service Accounts section of the Firebase console
-
Click Generate New Private Key to generate a new service account key file
The Repository
class integrates with mango, a plugin for mingo and
qs-to-mongo. This allows for aggregation pipelines and performing searches
with query criteria and options, as well as URL query handling.
mingo operators loaded by mango can be viewed in the config file. If additional operators are needed, you'll need to load them on your own before creating a new repository.
For shorter import paths, TypeScript users can add the following aliases:
{
"compilerOptions": {
"paths": {
"@dreepo": ["node_modules/@flex-development/dreepo/index"],
"@dreepo/*": ["node_modules/@flex-development/dreepo/*"]
}
}
}
These aliases will be used in following code examples.
Before creating a new repository, initialize a RepoDBConnection
provider to
establish a connection between your database and repository.
import { RepoDBConnection } from '@dreepo'
const path = 'users'
const url = process.env.FIREBASE_DATABASE_URL || ''
const client_email = process.env.FIREBASE_CLIENT_EMAIL || ''
const private_key = process.env.FIREBASE_PRIVATE_KEY || ''
export const dbconn = new RepoDBConnection(path, url, client_email, private_key)
Note:
- An
Exception
will be thrown if any options are invalid - Private keys will be formatted using
private_key.replace(/\\n/g, '\n')
Before instantiating a new repository, a model needs to be created.
For the next set of examples, the model User
will be used.
import { Entity } from '@dreepo'
import type { IEntity } from '@dreepo/interfaces'
import type { RepoParsedUrlQuery, RepoSearchParams } from '@dreepo/types'
import {
IsEmail,
IsNotEmpty,
IsOptional,
IsPhoneNumber,
IsString
} from 'class-validator'
export interface IUser extends IEntity {
email: string
first_name: string
last_name: string
phone?: string
}
export type UserParams = RepoSearchParams<IUser>
export type UserQuery = RepoParsedUrlQuery<IUser>
export class User extends Entity implements IUser {
@IsEmail()
email: IUser['email']
@IsString()
@IsNotEmpty()
first_name: IUser['first_name']
@IsString()
@IsNotEmpty()
last_name: IUser['last_name']
@IsOptional()
@IsPhoneNumber()
phone?: IUser['phone']
}
For more information about validation decorators, see the class-validator package.
Dreepo also exposes a set of custom decorators.
The Repository
class accepts an options
object that passes additional
options to mango, and class-transformer-validator.
import type { RepoOptionsDTO } from '@dreepo/dto'
const options: RepoOptionsDTO<IUser> = {
cache: { collection: [] },
mingo: {},
parser: {},
validation: {
enabled: true,
transformer: {},
validator: {}
}
}
Note that all properties are optional.
For more information about the cache
, mingo
, and parser
options, see
Plugin from the mango documentation. options.mingo.idKey
will be
overridden and always have the value 'id'
.
Validation options will be merged with the following object:
import type { TVODefaults } from '@dreepo/types'
/**
* @property {TVODefaults} TVO_DEFAULTS - `class-transformer-validator` options
* @see https://github.com/MichalLytek/class-transformer-validator
*/
export const TVO_DEFAULTS: TVODefaults = Object.freeze({
transformer: {},
validator: {
enableDebugMessages: true,
forbidNonWhitelisted: true,
stopAtFirstError: false,
validationError: { target: false, value: true },
whitelist: true
}
})
import { Repository } from '@dreepo'
export const Users = new Repository<IUser, UserParams, UserQuery>(
dbconn,
User,
options
)
After instantiation, before calling any repository methods, the repository's cache must be refreshed to keep the database and repository cache in sync.
If the cache is empty before running an aggregation pipeline or executing a search, a warning will be logged to the console.
Not refreshing the cache before a write operation (create
, patch
, or save
)
could lead to accidental overwrites or other database inconsistencies.
await Users.refreshCache()
The Repository
class allows users to perform CRUD operations on their Realtime
Database, as well as check values against the repository model schema.
Documentation can be viewed here.
/**
* `Repository` class interface.
*
* - https://github.com/flex-development/mango
* - https://github.com/fox1t/qs-to-mongo
* - https://github.com/kofrasa/mingo
*
* @template E - Entity
* @template P - Repository search parameters (query criteria and options)
* @template Q - Parsed URL query object
*/
export interface IRepository<
E extends IEntity = IEntity,
P extends RepoSearchParams<E> = RepoSearchParams<E>,
Q extends RepoParsedUrlQuery<E> = RepoParsedUrlQuery<E>
> extends IMango<E, EUID, P, Q> {
readonly cache: RepoCache<E>
readonly dbconn: IRepoDBConnection
readonly model: EntityClass<E>
readonly options: RepoOptions<E>
readonly validator: IRepoValidator<E>
clear(): Promise<boolean>
create(dto: EntityDTO<E>): Promise<E>
delete(id: OneOrMany<E['id']>, should_exist?: boolean): Promise<typeof id>
patch(id: E['id'], dto: Partial<EntityDTO<E>>, rfields?: string[]): Promise<E>
refreshCache(): Promise<RepoCache<E>>
save(dto: OneOrMany<OrPartial<EntityDTO<E>>>): Promise<E[]>
}
Looking for the Mango
plugin docs? See here.
- Axios - Promise based HTTP client
- Firebase Database REST API - REST API for Firebase Realtime Database
- Google Auth Library Node.js Client - Node.js library for Google OAuth2
- class-transformer-validator - Plugin for class-transformer and class-validator
- debug - Debugging utility
- mango - Plugin for mingo and qs-to-mongo