Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/flex-development/dreepo

:fire: Repository Pattern implementation for Firebase Realtime Database
https://github.com/flex-development/dreepo

class-transformer class-transformer-validator class-validator firebase-admin firebase-realtime-database mingo qs-to-mongo repository-pattern typescript

Last synced: about 2 months ago
JSON representation

:fire: Repository Pattern implementation for Firebase Realtime Database

Awesome Lists containing this project

README

        

# dreepo

Repository Pattern implementation for Firebase Realtime Database

[![TypeScript](https://badgen.net/badge/-/typescript?icon=typescript&label)](https://www.typescriptlang.org/)
[![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest)

## Overview

[Getting Started](#getting-started)
[Installation](#installation)
[Usage](#usage)
[Built With](#built-with)
[Contributing](docs/CONTRIBUTING.md)

## Getting Started

Inspired by [Fireorm][11], Dreepo exposes a Repository Pattern implementation
for [Firebase Realtime Database][2].

Alongside an abstract database access layer, repositories also support:

- aggregation pipelines, searches, and url queries using [mango][8]
- model validation using [class-validator][6]

## Installation

1. Create or edit an `.npmrc` file with the following information:

```utf-8
@flex-development:registry=https://npm.pkg.github.com/
```

2. Add project to `dependencies`

```zsh
yarn add @flex-development/dreepo # or npm i @flex-development/dreepo
```

## Usage

[Configuration](#configuration)
[Database Connection](#database-connection)
[Modeling Entities](#modeling-entities)
[Repository Options](#repository-options)
[Creating a New Repository](#creating-a-new-repository)
[Repository Cache](#repository-cache)
[Repository Class API](#repository-class-api)

### Configuration

#### Firebase Service Account

Dreepo communicates with your Realtime Database using the [Firebase Database
REST API][2]. Generated by service accounts, [Google OAuth2 access tokens][12]
are used to authenticate requests.

1. Navigate to the [Service Accounts][13] section of the Firebase console

2. Click **Generate New Private Key** to generate a new service account key file

#### Environment Variables

- `DEBUG`: Toggle [debug][7] logs from the `dreepo` namespace
- `DEBUG_COLORS`: Toggle [debug][7] log namespace colors

#### Mango

The `Repository` class integrates with [mango][8], a plugin for [mingo][9] and
[qs-to-mongo][10]. This allows for aggregation pipelines and performing searches
with query criteria and options, as well as URL query handling.

[mingo][9] operators loaded by [mango][8] can be viewed in the [config][14]
file. If additional operators are needed, you'll need to load them on your own
_before_ [creating a new repository](#creating-a-new-repository).

#### TypeScript

For shorter import paths, TypeScript users can add the following aliases:

```json
{
"compilerOptions": {
"paths": {
"@dreepo": ["node_modules/@flex-development/dreepo/index"],
"@dreepo/*": ["node_modules/@flex-development/dreepo/*"]
}
}
}
```

These aliases will be used in following code examples.

### Database Connection

Before creating a new repository, initialize a `RepoDBConnection` provider to
establish a connection between your database and repository.

```typescript
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')`

### Modeling Entities

Before instantiating a new repository, a model needs to be created.

For the next set of examples, the model `User` will be used.

```typescript
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
export type UserQuery = RepoParsedUrlQuery

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][6]
package.

Dreepo also exposes a set of [custom decorators](src/decorators/index.ts).

### Repository Options

The `Repository` class accepts an `options` object that passes additional
options to [mango][8], and [class-transformer-validator][4].

```typescript
import type { RepoOptionsDTO } from '@dreepo/dto'

const options: RepoOptionsDTO = {
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][15] from the [mango][8] documentation. `options.mingo.idKey` will be
overridden and always have the value `'id'`.

Validation options will be merged with the following object:

```typescript
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
}
})
```

### Creating a New Repository

```typescript
import { Repository } from '@dreepo'

export const Users = new Repository(
dbconn,
User,
options
)
```

### Repository Cache

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.

```typescript
await Users.refreshCache()
```

### Repository Class API

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](src/repositories/repository.ts).

```typescript
/**
* `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 = RepoSearchParams,
Q extends RepoParsedUrlQuery = RepoParsedUrlQuery
> extends IMango {
readonly cache: RepoCache
readonly dbconn: IRepoDBConnection
readonly model: EntityClass
readonly options: RepoOptions
readonly validator: IRepoValidator

clear(): Promise
create(dto: EntityDTO): Promise
delete(id: OneOrMany, should_exist?: boolean): Promise
patch(id: E['id'], dto: Partial>, rfields?: string[]): Promise
refreshCache(): Promise>
save(dto: OneOrMany>>): Promise
}
```

Looking for the `Mango` plugin docs? [See here][16].

## Built With

- [Axios][1] - Promise based HTTP client
- [Firebase Database REST API][2] - REST API for Firebase Realtime Database
- [Google Auth Library Node.js Client][3] - Node.js library for Google OAuth2
- [class-transformer-validator][4] - Plugin for [class-transformer][5] and
[class-validator][6]
- [debug][7] - Debugging utility
- [mango][8] - Plugin for [mingo][9] and [qs-to-mongo][10]

[1]: https://github.com/axios/axios
[2]: https://firebase.google.com/docs/reference/rest/database
[3]: https://github.com/googleapis/google-auth-library-nodejs
[4]: https://github.com/MichalLytek/class-transformer-validator
[5]: https://github.com/typestack/class-transformer
[6]: https://github.com/typestack/class-validator
[7]: https://github.com/visionmedia/debug
[8]: https://github.com/flex-development/mango
[9]: https://github.com/kofrasa/mingo
[10]: https://github.com/fox1t/qs-to-mongo
[11]: https://github.com/wovalle/fireorm
[12]: https://developers.google.com/identity/protocols/oauth2
[13]:
https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk
[14]: https://github.com/flex-development/mango/blob/next/src/config/mingo.ts
[15]: https://github.com/flex-development/mango#plugin
[16]:
https://github.com/flex-development/mango/blob/next/src/plugins/mango.plugin.ts