{"id":13601929,"url":"https://github.com/paddls/ngx-repository","last_synced_at":"2026-02-27T09:43:04.652Z","repository":{"id":38780172,"uuid":"223443889","full_name":"paddls/ngx-repository","owner":"paddls","description":"Easily create a strongly typed data client (Http REST or Firestore) in your Angular project.","archived":false,"fork":false,"pushed_at":"2024-07-16T12:46:48.000Z","size":4852,"stargazers_count":34,"open_issues_count":4,"forks_count":2,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-07-28T09:09:39.813Z","etag":null,"topics":["angular","firestore","rest-api","restful-api","strongly-typed","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@paddls/ngx-repository","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paddls.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-22T16:32:57.000Z","updated_at":"2024-08-01T18:42:38.392Z","dependencies_parsed_at":"2024-03-12T10:29:12.356Z","dependency_job_id":"e823abd1-abc5-4aa1-b80b-e212db726636","html_url":"https://github.com/paddls/ngx-repository","commit_stats":{"total_commits":238,"total_committers":10,"mean_commits":23.8,"dds":0.7058823529411764,"last_synced_commit":"80e3a46718e4b521ec2ff9561876cab2cd591278"},"previous_names":["witty-services/ngx-repository"],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-repository","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-repository/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-repository/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paddls%2Fngx-repository/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paddls","download_url":"https://codeload.github.com/paddls/ngx-repository/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223124292,"owners_count":17091184,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","firestore","rest-api","restful-api","strongly-typed","typescript"],"created_at":"2024-08-01T18:01:10.239Z","updated_at":"2026-02-27T09:43:04.645Z","avatar_url":"https://github.com/paddls.png","language":"TypeScript","funding_links":[],"categories":["angular","Architecture and Advanced Topics"],"sub_categories":["HTTP"],"readme":"# NGX-REPOSITORY\n\n![ngx-repository-ci](https://github.com/paddls/ngx-repository/workflows/build/badge.svg?branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/paddls/ngx-repository/badge.svg?branch=master)](https://coveralls.io/github/paddls/ngx-repository?branch=master)\n[![npm version](https://badge.fury.io/js/%40paddls%2Fngx-repository.svg)](https://badge.fury.io/js/%40paddls%2Fngx-repository)\n![GitHub](https://img.shields.io/github/license/paddls/ngx-repository)\n![GitHub repo size](https://img.shields.io/github/repo-size/paddls/ngx-repository)\n![GitHub last commit](https://img.shields.io/github/last-commit/paddls/ngx-repository)\n![GitHub issues](https://img.shields.io/github/issues/paddls/ngx-repository)\n![GitHub top language](https://img.shields.io/github/languages/top/paddls/ngx-repository)\n\nEasily create a **strongly typed data access layer** in your **Angular** project.\n\n## Summary\n\n* [How to install](#how-to-install)\n  * [Recommended Angular versions](#recommended-angular-versions)\n  * [Main module](#main-module)\n  * [Http Driver](#http-driver)\n  * [Firestore Driver](#firestore-driver)\n  * [Import Modules](#import-modules)\n* [Basic usage](#basic-usage)\n  * [Resource](#resource)\n    * [HttpResource and FirestoreResource](#httpresource-and-firestoreresource)\n    * [Id and Column](#id-and-column)\n  * [Repository](#repository)\n* [Id and Column configuration](#id-and-column-configuration)\n* [Path parameters and PathColumn](#path-parameters-and-pathcolumn)\n* [Fetch associated resource](#fetch-associated-resources)\n  * [JoinColumn](#joincolumn)\n  * [SubCollection](#subcollection)\n* [Query](#query)\n* [Resource configuration](#resource-configuration)\n* [HTTP Pagination](#http-pagination)\n  * [Page type](#page-type)\n  * [Page response processor](#page-response-processor)\n* [HttpLiveResource](#httpliveresource)\n* [@FirestoreCreatedAt and @FirestoreUpdatedAt](#firestorecreatedat-and-firestoreupdatedat)\n* [Advanced usage](#advanced-usage)\n  * [Custom repository](#custom-repository)\n  * [Custom HTTP API](#custom-http-api)\n  * [Event system](#event-system)\n* [Test and debug](#test-and-debug)\n  * [NgxRepositoryTestingModule](#ngxrepositorytestingmodule)\n  * [Enable debug mode](#enable-debug-mode)\n* [Install and build project](#install-and-build-project)\n\n## How to install\n\n### Recommended Angular versions\n\n| `Angular`          | `NgxRepository`   |\n|--------------------|-------------------|\n| `19.0.0` and above | `9.0.0` and above |\n| `18.0.0` and above | `8.0.0` and above |\n| `17.0.0` and above | `7.0.0` and above |\n| `16.0.0` and above | `6.0.0` and above |\n| `15.0.0` and above | `5.0.0` and above |\n| `14.0.0` and above | `4.0.1` and above |\n| `13.0.0` and above | `3.0.0` and above |\n| `8.0.0` and above  | `1.0.0` and above |\n\n### Main module\n\nFirst install the main library in your project :\n\n```shell script\nnpm install --save @paddls/ngx-repository\n```\n\nAfter that, choose drivers and install them as follows.\n\n### Http Driver\n\n```shell script\nnpm install --save @paddls/ngx-http-repository\n```\n\n### Firestore Driver\n\n```shell script\nnpm install --save @paddls/ngx-firestore-repository\n```\n\n### Import modules\n\nTo start using NgxRepository, provide it like this :\n\n```typescript\nimport { NgxRepositoryModule } from '@paddls/ngx-repository';\nimport { NgxHttpRepositoryModule } from '@paddls/ngx-http-repository';\nimport { NgxFirestoreRepositoryModule } from '@paddls/ngx-firestore-repository';\nimport { initializeFirestore } from 'firebase/firestore';\n\nconst firebaseApp: FirebaseApp = initializeApp({\n  apiKey: 'TODO',\n  authDomain: 'TODO',\n  databaseURL: 'TODO',\n  projectId: 'TODO',\n  storageBucket: 'TODO',\n  messagingSenderId: 'TODO',\n  appId: 'TODO',\n  measurementId: 'TODO'\n});\n\nconst firestore = initializeFirestore(firebaseApp, {\n  localCache: persistentLocalCache()\n});\n\nbootstrapApplication(AppComponent, {\n  providers: [\n    provideNgxRepository({\n      normalizerConfiguration: {\n        denormalizeNull: true,\n        normalizeNull: false,\n        denormalizeUndefined: true,\n        normalizeUndefined: false\n      }\n    }),\n    provideNgxHttpRepository({\n      debug: true\n    }),\n    provideNgxFirestoreRepository({\n      debug: true\n    }),\n  ]\n}).catch((err: Error) =\u003e console.error(err));\n```\n\n## Basic usage\n\nThis library abstracts the data access layer of your Angular project. You just need to provide some configuration thanks\nto typescript decorators to start using auto-generated repositories.\n\n### Resource\n\nStart by creating a Typescript class for the type of resource you need to manipulate with NgxRepository. Once this is\ndone, you can start annotating this class with the following decorators.\n\n#### HttpResource and FirestoreResource\n\nFirst, add a `@HttpResource()` or `@FirestoreResource()` decorator (depending on which protocol you wish to use)\non the resource class. The most basic configuration for this annotation consists in giving the HTTP or Firestore\nresource path of the resource.\n\n```typescript\nimport { FirestoreResource } from '@paddls/ngx-firestore-repository';\nimport { HttpResource } from '@paddls/ngx-http-repository';\n\n// for Http\n@HttpResource({\n  path: '/api/users'\n})\n// or for Firestore\n@FirestoreResource({\n  path: '/users'\n})\nexport class User {\n  // ...\n}\n```\n\n#### Id and Column\n\nThen, add an `@Id()` decorator on the resource id and `@Column()` decorators on each resource field you want mapped with\nNgxRepository.\n\n```typescript\nimport { FirestoreResource } from '@paddls/ngx-firestore-repository';\nimport { HttpResource } from '@paddls/ngx-http-repository';\nimport { Id, Column } from '@paddls/ngx-repository';\n\n// for Http\n@HttpResource({\n  path: '/api/users'\n})\n// or for Firestore\n@FirestoreResource({\n  path: '/users'\n})\nexport class User {\n\n  @Id() // define the resource id\n  public id: number;\n\n  @Column() // define a column\n  public firstName: string;\n\n  @Column()\n  public lastName: string;\n}\n```\n\n### Repository\n\nRight, now you have your resources. NgxRepository will generate all repositories on demand. You just have to inject it\nin your services using the\n`injectRepository()` function. You need to specify in the function args the type of your resource and the type of\nyour repository(`HttpRepository`, `FirestoreRepository`...).\n\nThe generic types of the generated repository type are the type of the resource and the type of the resource id.\n\n```typescript\nimport { injectRepository, Page } from '@paddls/ngx-repository';\nimport { HttpRepository } from '@paddls/ngx-http-repository'\n\n@Injectable()\nexport class BookService {\n\n  // repository is build with Http driver for User resource\n  private readonly bookRepository: HttpRepository\u003cBook, string\u003e = injectRepository({\n    resourceType: () =\u003e Book,\n    repository: () =\u003e HttpRepository\n  });\n\n  public findAll(): Observable\u003cPage\u003cBook\u003e\u003e {\n    return this.bookRepository.findAll();\n  }\n\n  // returns the id of created resource\n  public create(book: Book): Observable\u003cnumber\u003e {\n    return this.bookRepository.create(book);\n  }\n\n  // observable completes when update is effective\n  public update(book: Book): Observable\u003cvoid\u003e {\n    return this.bookRepository.update(book);\n  }\n}\n```\n\n\u003e ⚠️ Prior to version 9.1.0, the repository was injected using the `@InjectRepository()` decorator. This decorator is\n\u003e now deprecated and will be removed in a future version.\n\u003e Please use the `injectRepository()` function instead.\n\n\nEach repository of a resource made from `FirestoreDriver` or `HttpDriver` are singleton services stored in Angular\nInjector, so don't worry about injecting them on several services.\n\nLast but not least : NgxRepository automatically serializes and deserializes your resources between JSON and strongly\ntyped TypeScript classes. Note that only fields with\n`@Id()` or `@Column()` decorators are marked for serialization.\n\n## Id and Column configuration\n\nYou can add configuration to `@Id()` and `@Column()` decorators as follows.\n\n```typescript\nimport { HttpResource } from '@paddls/ngx-http-repository';\nimport { Id, Column, DateConverter } from '@paddls/ngx-repository';\n\n@HttpResource({\n  path: '/api/users'\n})\nexport class User {\n\n  @Id() // define the resource id\n  public id: number;\n\n  @Column() // define a column\n  public firstName: string;\n\n  @Column('lastname')  // define a column with special mapping\n  public lastName: string;\n\n  @Column(() =\u003e Address) // define a column with a child model\n  public address: Address;\n\n  @Column({type: () =\u003e Job, field: 'job'}) // combine model and special mapping\n  public myJob: Job;\n\n  @Column({field: 'createdAt', customConverter: () =\u003e DateConverter}) // use custom converter for special type\n  public createdAt: Date;\n}\n```\n\nAs shown in the example above, each configuration field is optional : you can define any field you want or not to have\nany configuration at all.\n\n| Field                  | Description                                                                                                     | Available on `@Id()` |\n|------------------------|-----------------------------------------------------------------------------------------------------------------|----------------------|\n| `field`                | Field name in JSON                                                                                              | Yes                  |\n| `type`                 | Field type after deserialization : only fields with `@Id()` or `@Column()` decorator in type will be serialized | No                   |\n| `readOnly`             | Boolean to indicate to not send the value in json to the server                                                 | Yes                  |\n| `writeOnly`            | Boolean to indicate to ignore the field in json                                                                 | Yes                  |\n| `customConverter`      | A converter to make a custom serialization/deserialization                                                      | No                   |\n| `denormalizeNull`      | Boolean to override global configuration to denormalize the column when is set to null value                    | Yes                  |\n| `denormalizeUndefined` | Boolean to override global configuration to denormalize the column when is set to undefined value               | Yes                  |\n| `normalizeNull`        | Boolean to override global configuration to normalize the column when is set to null value                      | Yes                  |\n| `normalizeUndefined`   | Boolean to override global configuration to normalize the column when is set to undefined value                 | Yes                  |\n|\n\nThe `field` and `type` fields can be defined using a shorthand. For `field`, just specify a string directly in the\ncontext, it will be interpreted as such. Same thing for `type` : specify directly an anonymous function returning the\ntype in the context\n(as shown in the example).\n\n## Path parameters and PathColumn\n\nYou can add parameters on a resource path using `:` character. You can map those parameters with values using\nthe [query system](#query).\n\n`@PathColumn()` decorator allows you to retrieve the path parameter value of a resource and map it to the decorated\nfield.\n\n```typescript\nimport { HttpResource } from '@paddls/ngx-http-repository';\nimport { Id, Column, PathColumn } from '@paddls/ngx-repository';\n\n@HttpResource({\n  path: '/libraries/:libraryId/books'\n})\nexport class Book {\n\n  @Id()\n  public id: number;\n\n  @Column()\n  public title: string;\n\n  @PathColumn()\n  public libraryId: string;\n\n  // or\n\n  @PathColumn('libraryId')\n  public theLibraryId: string;\n\n}\n```\n\n## Fetch associated resources\n\n### JoinColumn\n\nYou can fetch associated resources using `JoinColumn`.\n\n```typescript\nimport { HttpResource } from '@paddls/ngx-http-repository';\nimport { Id, Column, JoinColumn } from '@paddls/ngx-repository';\nimport { FirestoreRepository } from '@paddls/ngx-firestore-repository';\n\n@HttpResource({\n  path: '/libraries/:libraryId/books'\n})\nexport class Book {\n\n  @Id()\n  public id: number;\n\n  @Column()\n  public title: string;\n\n  @Column()\n  public authorId: string;\n\n  // initialize the request to get associated author, using instance attribute 'authorId' with User FirestoreRepository\n  @JoinColumn({attribute: 'authorId', resourceType: () =\u003e User, repository: () =\u003e FirestoreRepository})\n  public author$: Observable\u003cPerson\u003e; // data will be lazy fetched on subscribe\n}\n```\n\n### SubCollection\n\nYou can fetch associated resources using `SubCollection`.\n\n```typescript\nimport { HttpResource, HttpRepository } from '@paddls/ngx-http-repository';\nimport { Id, Column, SubCollection } from '@paddls/ngx-repository';\n\n@HttpResource({\n  path: '/libraries/:libraryId/books'\n})\nexport class Book {\n\n  @Id()\n  public id: number;\n\n  @Column()\n  public title: string;\n\n  // you should specify the corresponding resource, how to request resources and the repository to use\n  @SubCollection({\n    resourceType: () =\u003e Comment,\n    // params are extra information context request (for example libraryId in path) \n    params: (book: Book, params: any) =\u003e new CommentQuery({bookId: book.id, libraryId: params.libraryId}),\n    repository: () =\u003e HttpRepository\n  })\n  public comments$: Observable\u003cComment[]\u003e;\n}\n```\n\n## Query\n\nWith NgxRepository, a Query is an object holding some informations associated with the querying of one or several\nresources. You can then provide an instance of this Query as a parameter of any method available on `HttpRepository`\nor `FirestoreRepository`.\n\nHere is an example of a query for a `@HttpResource()` :\n\n```typescript\nimport { HttpQueryParam, HttpHeader } from '@paddls/ngx-http-repository';\nimport { PathParam } from '@paddls/ngx-repository';\n\nexport class BookQuery {\n\n  // use http query param\n\n  @HttpQueryParam() // param is forwarded into http query param\n  public title: string;\n\n  // or\n\n  @HttpQueryParam('title')\n  public theTitle: string;\n\n  // use path param\n\n  @PathParam() // param replace :libraryId into resource path\n  public libraryId: string;\n\n  // or\n\n  @PathParam('libraryId')\n  public library: string;\n\n  // use http header\n\n  @HttpHeader() // path is forwarded into http header\n  public page: number = 1;\n\n  // or\n\n  @HttpHeader('itemPerPage')\n  public size: number = 2;\n\n  public constructor(data: Partial\u003cBookQuery\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n```\n\nAnd an example of a query for a `@FirestoreResource()` :\n\n```typescript\nimport {\n  FirestoreCriteria,\n  FirestoreEndAt,\n  FirestoreEndBefore,\n  FirestoreLimit,\n  FirestoreLimitToLast,\n  FirestoreOrderBy,\n  FirestoreOrderByContext,\n  FirestoreStartAfter,\n  FirestoreStartAt\n} from '@paddls/ngx-firestore-repository';\n\nexport class ClientQuery {\n\n  @FirestoreCriteria({field: 'lastName', operator: '=='})\n  public lastNameEqual?: string;\n\n  @FirestoreOrderBy()\n  public orderBy?: string | FirestoreOrderByContext | (FirestoreOrderByContext | string)[];\n\n  @FirestoreStartAt()\n  public startAt?: any;\n\n  @FirestoreStartAfter()\n  public startAfter?: any;\n\n  @FirestoreEndAt()\n  public endAt?: any;\n\n  @FirestoreEndBefore()\n  public endBefore?: any;\n\n  @FirestoreLimit()\n  public limit?: number;\n\n  @FirestoreLimitToLast()\n  public limitToLast?: number;\n\n  public constructor(data: Partial\u003cClientQuery\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n```\n\nThe following table lists all the type of fields you can add to a query object and with which repository they are\navailable.\n\n| Decorator                 | Description                                                       | Repository type                         |\n|---------------------------|-------------------------------------------------------------------|-----------------------------------------|\n| `@PathParam()`            | Replaces path parameter with field value                          | `HttpRepository`, `FirestoreRepository` |\n| `@HttpQueryParam()`       | Adds a query param to the HTTP request (eg. `/users/?name=Oscar`) | `HttpRepository`                        |\n| `@HttpHeader()`           | Adds a HTTP header to the request with field value                | `HttpRepository`                        |\n| `@FirestoreCriteria()`    | Adds a Firestore query criteria                                   | `FirestoreRepository`                   |\n| `@FirestoreOrderBy()`     | Adds a `.orderBy()` clause to Firestore request                   | `FirestoreRepository`                   |\n| `@FirestoreLimit()`       | Adds a `.limit()` clause to Firestore request                     | `FirestoreRepository`                   |\n| `@FirestoreLimitToLast()` | Adds a `.limitToLast()` clause to Firestore request               | `FirestoreRepository`                   |\n| `@FirestoreStartAt()`     | Adds a `.startAt()` query cursor to Firestore request             | `FirestoreRepository`                   |\n| `@FirestoreStartAfter()`  | Adds a `.startAfter()` query cursor to Firestore request          | `FirestoreRepository`                   |\n| `@FirestoreEndAt()`       | Adds a `.endAt()` query cursor to Firestore request               | `FirestoreRepository`                   |\n| `@FirestoreEndBefore()`   | Adds a `.endBefore()` query cursor to Firestore request           | `FirestoreRepository`                   |\n\nThe following example shows a query used in a `findAll()` operation on a Firestore resource.\n\n```typescript\n\n@Injectable()\nexport class ClientService {\n\n  private readonly repository: FirestoreRepository\u003cClient, string\u003e = injectRepository({\n    resourceType: () =\u003e Client,\n    repository: () =\u003e FirestoreRepository\n  });\n\n  public searchByLastName(searchedLastName: string): Observable\u003cPage\u003cClient\u003e\u003e {\n    return this.repository.findAll(new ClientQuery({\n      lastNameEqual: searchedLastName,\n      orderBy: ['firstName']\n    }));\n  }\n}\n```\n\n## Resource configuration\n\nYou can configure your resource to your needs by adding context to `@HttpResource()`\nand `@FirestoreResource()` resource decorators.\n\n\u003e For example, you may need to specify a different path to your resource depending on\n\u003e the operation. This can be done by using the syntax in the following example.\n\n```typescript\n\n@HttpResource({\n  path: '/libraries',\n  update: '/library',\n})\nexport class Library {\n\n}\n```\n\nFor each operation execution, NgxRepository will look for a specific context attached to the operation for the resource.\nIf no specific context is found, the default context\n(here the `path`) will be used.\n\nThe available configuration is different depending on the type of resource and the targeted operation.\n\nThe targetable operations are the following :\n\n* `findById`\n* `findOne`\n* `findAll`\n* `create`\n* `update`\n* `patch`\n* `delete`\n* `read`\n* `write`\n\n**Response type**\n\nDefine a specific response type (different from the resource) using `responseType`\ncontext parameter.\n\n```typescript\n@HttpResource({\n  path: '/libraries',\n  write: {\n    responseType: () =\u003e WriteLibraryDto\n  }\n})\nexport class Library {\n\n}\n```\n\n**Http Response type**\n\nDefine a specific http response type for HttpClient service (different from the resource) using `httpResponseType`\ncontext parameter.\n\n```typescript\n@HttpResource({\n  path: '/libraries',\n  write: {\n    httpResponseType: 'blob'\n  }\n})\nexport class Library {\n\n}\n```\n\n**Path**\n\nDefine a specific path for an operation using `path` context parameter. Passing a string directly as an operation\ncontext is also possible to define the `path` context parameter.\n\n\u003e ⚠️ This context parameter is only available for `@HttpResource()`\n\n```typescript\n\n@HttpResource({\n  path: '/libraries',\n  create: {\n    path: '/create-a-library'\n  },\n  update: '/update-a-library'\n})\nexport class Library {\n\n}\n```\n\n**Page response processor**\n\nDefine a specific [page response processor](#page-response-processor) using\n`pageResponseProcessor` context parameter.\n\n\u003e ⚠️ This context parameter is only available for `findAll` operation and\n\u003e `@HttpResource()`\n\n```typescript\n\n@HttpResource({\n  path: '/libraries',\n  findAll: {\n    pageResponseProcessor: MyPageResponseProcessor\n  }\n})\nexport class Library {\n\n}\n```\n\n**Global configuration**\n\nIt is also possible to add some global configuration by adding it directly in the\n`forRoot()` method of your driver module import.\n\n## HTTP Pagination\n\n\u003e ⚠️ This feature is only available for `@HttpResource()`\n\n### Page type\n\nNgxRepository comes with a prebuilt HTTP pagination system. If a `@HttpResource()` is paginated server-side, the server\nusually sends in the HTTP response some context about the data being sent (page number, total number of elements and\npage size). NgxRepository holds these infos in a `Page` object.\n\nThe `Page` class extends the `Array` type and exposes additional infos about eventual server-side pagination.\n\nThis is the implementation of the `Page` type :\n\n```typescript\nexport class Page\u003cT = any\u003e extends Array\u003cT\u003e {\n\n  public currentPage: number;\n\n  public itemsPerPage: number;\n\n  public totalItems: number;\n}\n```\n\nThe `findAll` method of `HttpRepository` returns a `Observable\u003cPage\u003cT\u003e\u003e`\nwhere `T` is your resource. You can use it as an `Array` or as a `Page`\nif you want to access the pagination infos.\n\n### Page response processor\n\nBy default, the fields of the `Page` object returned by the\n`findAll`method of `HttpRepository` will be equal to :\n\n* `0` for the `currentPage` field\n* `items.length` for the `itemsPerPage` and `totalItems` fields\n\nThis is because the pagination infos can be returned in different ways (sometimes in the response headers, sometimes in\nthe body... ). To indicate how the server returns these elements, you have to create a `PageResponseProcessor`.\n\n```typescript\nimport { Injectable } from '@angular/core';\nimport { Page, RequestManagerContext, ResponseProcessor } from '@paddls/ngx-repository';\nimport { HttpRepositoryResponse } from '@paddls/ngx-http-repository';\n\n@Injectable()\nexport class MyPageResponseProcessor implements ResponseProcessor {\n\n  public transform(response: any, origin: HttpRepositoryResponse, context: RequestManagerContext): any {\n    const totalItems: number = parseInt(origin.getHeaders().get('apiTotalItems'), 10);\n    const itemsPerPage: number = parseInt(origin.getHeaders().get('apiPerPage'), 10);\n    const currentPage: number = parseInt(origin.getHeaders().get('apiCurrentPage'), 10);\n\n    return Page.build(response, currentPage, itemsPerPage, totalItems);\n  }\n}\n```\n\nA `PageResponseProcessor` implements the `ResponseProcessor` interface. This interface exposes a `transform()` method\nwhich transforms the HTTP response. In this method, you can build a `Page` object using the infos about the HTTP\nresponse provided in the `transform()` method parameters.\n\nThen, provide this processor in your `AppModule` and add it to any `findAll` configuration you need. It can be on a\n[specific `HttpResource()` or globally](#resource-configuration).\n\n## HttpLiveResource\n\n\u003e ⚠️ This feature is only available for `@HttpResource()`\n\nIf you want to build a truly reactive app, you may need the data displayed in your app to always be in sync with\nserver's data. This can be achieved by sending a new GET request each time a POST, PUT, PATCH or DELETE request is made.\n\nBy annotating your model with ``@HttpLiveResource()`` decorator, each observable returned by ``findAll()``\n, ``findOne()`` or ``findById()`` method will be transformed into a hot observable that will emit a new value each time\na writing method is called on the repository.\n\n```typescript\nimport { HttpLiveResource, HttpResource } from '@paddls/ngx-http-repository';\n\n@HttpLiveResource()\n@HttpResource('/libraries')\nexport class Library {\n}\n```\n\n## Multipart\n\n\u003e ⚠️ This feature is only available for `@HttpResource()`\n\nTo enable multipart, you should specify `multipart`field from `write`, `create`, `update`, `patch`, `delete`.\nThen you can specified each part on field using `@HttpMultipartColumn` decorator.\n\n```typescript\nimport { HttpMultipartColumn, HttpResource } from '@paddls/ngx-http-repository';\n\n@HttpResource({\n    path: '/libraries',\n    write: {\n      multipart: 'partNameOfLibrary',\n    }\n})\nexport class Library {\n\n    @HttpMultipartColumn('partNameOfFile')\n    public file: File\n}\n```\n\n## FirestoreCreatedAt and FirestoreUpdatedAt\n\nUse `@FirestoreCreatedAt()` and `@FirestoreUpdatedAt()` decorators on a `@FirestoreResource()` field to automatically\ninject creation and last update date of each resource firestore document.\n\nYou can also retrieve information for a particular resource field by adding its property name directly in the decorator\ncontext.\n\n```typescript\n@FirestoreResource({\n  path: '/clients'\n})\nexport class Client {\n\n  @Id()\n  public id: string;\n\n  @Column()\n  public name: string;\n\n  @FirestoreCreatedAt()\n  public createdAt: Date;\n\n  @FirestoreUpdatedAt('name')\n  public updatedAt: Date;\n\n  public constructor(data: Partial\u003cClient\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n\n```\n\n## Advanced usage\n\n### Custom Repository\n\nIn specific case, you can define a custom repository like that :\n\n```typescript\nimport { Repository } from '@paddls/ng-repository';\nimport { HttpRepository, HTTP_REPOSITORY_CONFIGURATION } from '@paddls/ng-http-repository';\n\n@Injectable()\n@Repository(() =\u003e Person)\nexport class PersonRepository extends HttpRepository\u003cPerson, string\u003e {\n\n  // use global configuration\n  public constructor(requestManager: RequestManager,\n                     driver: HttpRepositoryDriver,\n                     @Inject(HTTP_REPOSITORY_CONFIGURATION)\n                     configuration: ResourceConfiguration) {\n    super(requestManager, driver, configuration);\n  }\n\n  // overide global configuration\n  public constructor(requestManager: RequestManager,\n                     driver: HttpRepositoryDriver) {\n    super(requestManager, driver, {\n      // ... my configuration here\n    });\n  }\n\n  public searchByFirstName(searchedFirstName: string): Observable\u003cPerson[]\u003e {\n    // write your custom logic here\n  }\n}\n\n@Injectable()\nexport class PersonService {\n\n  // Like a standard service, you can inject your custom repository\n  public constructor(private readonly personRepository: PersonRepository) {\n  }\n}\n```\n\n### Custom HTTP API\n\nSometimes, you may use an HTTP API that does not fit REST standards. For example, you may want to send GET requests with\nbodies or requests with a response type different from body type.\n\nTo do that, simply create an ``@Injectable()`` class and define your API methods inside like in the following example :\n\n```typescript\n@Injectable()\nclass Api {\n\n  public get: HttpQueryFn\u003cApiResponse\u003e = httpGet\u003cApiResponse\u003e('/api', () =\u003e ApiResponse);\n\n  public getWithBody: HttpBodyFn\u003cApiResponse\u003e = httpGet({\n    path: '/api',\n    withBody: true\n  }, () =\u003e ApiResponse);\n\n  public getWithResponseProcessor: HttpQueryFn\u003cPage\u003cApiResponse\u003e\u003e = httpGet({\n    path: '/api',\n    postResponseProcessors: PageResponseProcessor\n  }, () =\u003e ApiResponse);\n\n  public getWithPathParam: HttpQueryFn\u003cApiResponse\u003e = httpGet('/api/:version', () =\u003e ApiResponse);\n\n  public post: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPost('/api', () =\u003e ApiResponse);\n\n  public postWithResponseProcessor: HttpBodyFn\u003cPage\u003cApiResponse\u003e\u003e = httpPost({\n    path: '/api',\n    postResponseProcessors: [PageResponseProcessor]\n  }, () =\u003e ApiResponse);\n\n  public postWithoutBody: HttpQueryFn\u003cPage\u003cApiResponse\u003e\u003e = httpPost({\n    path: '/api',\n    withBody: false\n  }, () =\u003e ApiResponse);\n\n  public postWithPathParam: HttpBodyFn\u003cApiResponse\u003e = httpPost('/api/:version', () =\u003e ApiResponse);\n\n  public put: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPut('/api', () =\u003e ApiResponse);\n\n  public putWithPathParam: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPut('/api/:version', () =\u003e ApiResponse);\n\n  public putWithoutBody: HttpQueryFn\u003cApiResponse, ApiRequest\u003e = httpPut({\n    path: '/api',\n    withBody: false\n  }, () =\u003e ApiResponse);\n\n  public putWithResponseProcessor: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPut({\n    path: '/api',\n    postResponseProcessors: [PageResponseProcessor]\n  }, () =\u003e ApiResponse);\n\n  public patch: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPatch('/api', () =\u003e ApiResponse);\n\n  public patchWithPathParam: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPatch('/api/:version', () =\u003e ApiResponse);\n\n  public patchWithoutBody: HttpQueryFn\u003cApiResponse, ApiRequest\u003e = httpPatch({\n    path: '/api',\n    withBody: false\n  }, () =\u003e ApiResponse);\n\n  public patchWithResponseProcessor: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpPatch({\n    path: '/api',\n    postResponseProcessors: [PageResponseProcessor]\n  }, () =\u003e ApiResponse);\n\n  public delete: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpDelete('/api', () =\u003e ApiResponse);\n\n  public deleteWithPathParam: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpDelete('/api/:version', () =\u003e ApiResponse);\n\n  public deleteWithoutBody: HttpQueryFn\u003cApiResponse, ApiRequest\u003e = httpDelete({\n    path: '/api',\n    withBody: false\n  }, () =\u003e ApiResponse);\n\n  public deleteWithResponseProcessor: HttpBodyFn\u003cApiResponse, ApiRequest\u003e = httpDelete({\n    path: '/api',\n    postResponseProcessors: [PageResponseProcessor]\n  }, () =\u003e ApiResponse);\n\n  public option: HttpQueryFn\u003cApiResponse\u003e = httpOption('/api', () =\u003e ApiResponse);\n\n  public optionWithPathParam: HttpQueryFn\u003cApiResponse\u003e = httpOption('/api/:version', () =\u003e ApiResponse);\n\n  public optionWithBody: HttpBodyFn\u003cApiResponse\u003e = httpOption({\n    path: '/api',\n    withBody: true\n  }, () =\u003e ApiResponse);\n\n  public optionWithResponseProcessor: HttpQueryFn\u003cApiResponse\u003e = httpOption({\n    path: '/api',\n    postResponseProcessors: [PageResponseProcessor]\n  }, () =\u003e ApiResponse);\n\n}\n```\n\nIn this example, ``ApiRequest`` and ``ApiResponse`` are typescript classes with an ``@Id()`` field and ``Column()``\nfields :\n\n```typescript\n  class ApiResponse {\n\n  @Id()\n  public id: number;\n\n  @Column()\n  public column: string;\n\n  @PathColumn()\n  public version: string;\n\n  public constructor(data: Partial\u003cApiResponse\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n\nclass ApiRequest {\n\n  @Id()\n  public identifier: number;\n\n  @Column()\n  public name: string;\n\n  public constructor(data: Partial\u003cApiRequest\u003e = {}) {\n    Object.assign(this, data);\n  }\n}\n```\n\nThen, inject your ``Api`` like any other service and make requests by calling the previously defined methods.\n\n### Event system\n\n``NgxRepository`` comes with a custom event system. Events are published during a request lifecycle, and you can use\nevent listeners to add custom logic when these events are published.\n\n```typescript\n@Injectable()\n@EventListener()\nexport class LogEventListener implements Listener\u003cany\u003e {\n\n  public on(event: any): void {\n    console.log(event);\n  }\n}\n```\n\nAn event listener is a class decorated with ``@EventListener``. You can pass a predicate or an array of predicates to\nthe decorator to filter the events you want to listen to.\n\nHere is the list of all events produced in ``NgxRepository`` :\n\n| Event                                | Repository type       |\n|--------------------------------------|-----------------------|\n| `BeforeExecuteHttpRequestEvent`      | `HttpRepository`      |\n| `BeforeHttpCreateEvent`              | `HttpRepository`      |\n| `BeforeHttpDeleteEvent`              | `HttpRepository`      |\n| `BeforeHttpFindAllEvent`             | `HttpRepository`      |\n| `BeforeHttpFindByIdEvent`            | `HttpRepository`      |\n| `BeforeHttpFindOneEvent`             | `HttpRepository`      |\n| `BeforeHttpPatchEvent`               | `HttpRepository`      |\n| `BeforeHttpUpdateEvent`              | `HttpRepository`      |\n| `AfterHttpCreateEvent`               | `HttpRepository`      |\n| `AfterHttpDeleteEvent`               | `HttpRepository`      |\n| `AfterHttpFindAllEvent`              | `HttpRepository`      |\n| `AfterHttpFindByIdEvent`             | `HttpRepository`      |\n| `AfterHttpFindOneEvent`              | `HttpRepository`      |\n| `AfterHttpPatchEvent`                | `HttpRepository`      |\n| `AfterHttpUpdateEvent`               | `HttpRepository`      |\n| `AfterExecuteHttpRequestEvent`       | `HttpRepository`      |\n| `BeforeExecuteFirestoreRequestEvent` | `FirestoreRepository` |\n| `BeforeFirestoreCreateEvent`         | `FirestoreRepository` |\n| `BeforeFirestoreDeleteEvent`         | `FirestoreRepository` |\n| `BeforeFirestoreFindAllEvent`        | `FirestoreRepository` |\n| `BeforeFirestoreFindByIdEvent`       | `FirestoreRepository` |\n| `BeforeFirestoreFindOneEvent`        | `FirestoreRepository` |\n| `BeforeFirestorePatchEvent`          | `FirestoreRepository` |\n| `BeforeFirestoreUpdateEvent`         | `FirestoreRepository` |\n| `AfterFirestoreCreateEvent`          | `FirestoreRepository` |\n| `AfterFirestoreDeleteEvent`          | `FirestoreRepository` |\n| `AfterFirestoreFindAllEvent`         | `FirestoreRepository` |\n| `AfterFirestoreFindByIdEvent`        | `FirestoreRepository` |\n| `AfterFirestoreFindOneEvent`         | `FirestoreRepository` |\n| `AfterFirestorePatchEvent`           | `FirestoreRepository` |\n| `AfterFirestoreUpdateEvent`          | `FirestoreRepository` |\n| `AfterExecuteFirestoreRequestEvent`  | `FirestoreRepository` |\n\n## Test and debug\n\n### NgxRepositoryTestingModule\n\nYou can easily test any service using ``NgxRepository`` using ``NgxRepositoryTestingModule`` like in the following\nexample :\n\n```typescript\nimport { TestBed } from '@angular/core/testing';\nimport { MockRepository, NgxRepositoryTestingModule, Page } from '@paddls/ngx-repository';\nimport { LibraryService } from './library.service';\nimport { Library } from '../model/library.model';\nimport { HttpRepository } from '@paddls/ngx-http-repository';\nimport { LibraryQuery } from '../query/library.query';\n\ndescribe('LibraryService', () =\u003e {\n  let libraryService: LibraryService;\n  let libraryRepository: MockRepository;\n\n  beforeEach(() =\u003e {\n    TestBed.configureTestingModule({\n      imports: [\n        NgxRepositoryTestingModule.forTest()\n      ],\n      providers: [\n        LibraryService\n      ]\n    });\n\n    libraryRepository = NgxRepositoryTestingModule.getRepository(Library, HttpRepository);\n    libraryService = TestBed.get(LibraryService);\n  });\n\n  describe('#findAll', () =\u003e {\n    it('should call findAll from read repository', (done: DoneFn) =\u003e {\n      spyOn(libraryRepository, 'findAll').and.callThrough();\n\n      libraryService.findAll(1, 5).subscribe((page: Page) =\u003e {\n        expect(libraryRepository.findAll).toHaveBeenCalledWith(new LibraryQuery({\n          opened: true,\n          page: 1,\n          itemPerPage: 5\n        }));\n\n        expect(page.length).toBe(0);\n\n        done();\n      });\n\n      libraryRepository.emit('findAll', Page.build([]));\n      expect(libraryRepository.findAll).toHaveBeenCalledWith(new LibraryQuery({\n        opened: true,\n        page: 1,\n        itemPerPage: 5\n      }));\n    });\n  });\n});\n\n```\n\n### Enable debug mode\n\nYou can enable debug mode by setting ``debug`` flag to ``true`` in your repository module import config.\n\n```typescript\n\n@NgModule({\n  imports: [\n    BrowserModule,\n    CoreModule,\n    NgxRepositoryModule.forRoot(),\n    NgxFirestoreRepositoryModule.forRoot({\n      debug: true\n    }),\n    NgxHttpRepositoryModule.forRoot({\n      debug: true\n    }),\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule {\n}\n```\n\nThen, open your console to see debug messages for each request made.\n\n## Install and build project\n\nTo install and build the project, you just have to clone the repository and make the dependency installation :\n\n````shell script\nnpm i\n````\n\nAfter dependency installation, you can run others commands :\n\n````shell script\n# Run the tests\nnpm run test\n\n# Run the linter\nnpm run lint\n\n# Run the example app\nnpm run start\n````\n\n## Release\n\nYou should release from the master branch\n\n````shell script\n# create the release\nnpm run release --release=x.x.x\n````\n\nThen push.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaddls%2Fngx-repository","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaddls%2Fngx-repository","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaddls%2Fngx-repository/lists"}