npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@paddls/ngx-http-repository

v8.2.0

Published

![ngx-repository-ci](https://github.com/paddls/ngx-repository/workflows/build/badge.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/github/paddls/ngx-repository/badge.svg?branch=master)](https://coveralls.io/github/paddls/ngx-repository?

Downloads

937

Readme

NGX-REPOSITORY

ngx-repository-ci Coverage Status npm version GitHub GitHub repo size GitHub last commit GitHub issues GitHub top language

Easily create a strongly typed data access layer in your Angular project.

Summary

How to install

Recommended Angular versions

| Angular | NgxRepository | |--------------------|-------------------| | 18.0.0 and above | 8.0.0 and above | | 17.0.0 and above | 7.0.0 and above | | 16.0.0 and above | 6.0.0 and above | | 15.0.0 and above | 5.0.0 and above | | 14.0.0 and above | 4.0.1 and above | | 13.0.0 and above | 3.0.0 and above | | 8.0.0 and above | 1.0.0 and above |

Main module

First install the main library in your project :

npm install --save @paddls/ngx-repository

After that, choose drivers and install them as follows.

Http Driver

npm install --save @paddls/ngx-http-repository

Firestore Driver

npm install --save @paddls/ngx-firestore-repository

Import modules

To start using NgxRepository, import NgxRepositoryModule and the modules corresponding to the chosen drivers :

import { NgxRepositoryModule } from '@paddls/ngx-repository';
import { NgxHttpRepositoryModule } from '@paddls/ngx-http-repository';
import { NgxFirestoreRepositoryModule } from '@paddls/ngx-firestore-repository';
import {initializeFirestore} from 'firebase/firestore';

const firebaseApp: FirebaseApp = initializeApp({
  apiKey: 'TODO',
  authDomain: 'TODO',
  databaseURL: 'TODO',
  projectId: 'TODO',
  storageBucket: 'TODO',
  messagingSenderId: 'TODO',
  appId: 'TODO',
  measurementId: 'TODO'
});

const firestore = initializeFirestore(firebaseApp, {
  localCache: persistentLocalCache()
});

@NgModule({
  imports: [
    NgxRepositoryModule.forRoot(),
    NgxHttpRepositoryModule.forRoot(), // Http driver
    NgxFirestoreRepositoryModule.forRoot({
      firestore 
    }), // Firestore driver
  ]
})
export class AppModule {
}

Basic usage

This library abstracts the data access layer of your Angular project. You just need to provide some configuration thanks to typescript decorators to start using auto-generated repositories.

Resource

Start by creating a Typescript class for the type of resource you need to manipulate with NgxRepository. Once this is done, you can start annotating this class with the following decorators.

HttpResource and FirestoreResource

First, add a @HttpResource() or @FirestoreResource() decorator (depending on which protocol you wish to use) on the resource class. The most basic configuration for this annotation consists in giving the HTTP or Firestore resource path of the resource.

import { FirestoreResource } from '@paddls/ngx-firestore-repository';
import { HttpResource } from '@paddls/ngx-http-repository';

// for Http
@HttpResource({
  path: '/api/users'
})
// or for Firestore
@FirestoreResource({
  path: '/users'
})
export class User {
  // ...
}

Id and Column

Then, add an @Id() decorator on the resource id and @Column() decorators on each resource field you want mapped with NgxRepository.

import { FirestoreResource } from '@paddls/ngx-firestore-repository';
import { HttpResource } from '@paddls/ngx-http-repository';
import { Id, Column } from '@paddls/ngx-repository';

// for Http
@HttpResource({
  path: '/api/users'
})
// or for Firestore
@FirestoreResource({
  path: '/users'
})
export class User {

  @Id() // define the resource id
  public id: number;

  @Column() // define a column
  public firstName: string;

  @Column()
  public lastName: string;
}

Repository

Right, now you have your resources. NgxRepository will generate all repositories on demand. You just have to inject it in your services using the @InjectRepository() decorator. You need to specify in the decorator context the type of your resource and the type of your repository(HttpRepository, FirestoreRepository...).

The generic types of the generated repository type are the type of the resource and the type of the resource id.

import { InjectRepository, Page } from '@paddls/ngx-repository';
import { HttpRepository } from '@paddls/ngx-http-repository'

@Injectable()
export class BookService {

  // repository is build with Http driver for User resource
  @InjectRepository({resourceType: () => Book, repository: () => HttpRepository})
  private readonly bookRepository: HttpRepository<Book, number>;

  public findAll(): Observable<Page<Book>> {
    return this.bookRepository.findAll();
  }

  // returns the id of created resource
  public create(book: Book): Observable<number> {
    return this.bookRepository.create(book);
  }

  // observable completes when update is effective
  public update(book: Book): Observable<void> {
    return this.bookRepository.update(book);
  }
}

Each repository of a resource made from FirestoreDriver or HttpDriver are singleton services stored in Angular Injector, so don't worry about injecting them on several services.

Last but not least : NgxRepository automatically serializes and deserializes your resources between JSON and strongly typed TypeScript classes. Note that only fields with @Id() or @Column() decorators are marked for serialization.

Id and Column configuration

You can add configuration to @Id() and @Column() decorators as follows.

import { HttpResource } from '@paddls/ngx-http-repository';
import { Id, Column, DateConverter } from '@paddls/ngx-repository';

@HttpResource({
  path: '/api/users'
})
export class User {

  @Id() // define the resource id
  public id: number;

  @Column() // define a column
  public firstName: string;

  @Column('lastname')  // define a column with special mapping
  public lastName: string;

  @Column(() => Address) // define a column with a child model
  public address: Address;

  @Column({type: () => Job, field: 'job'}) // combine model and special mapping
  public myJob: Job;

  @Column({field: 'createdAt', customConverter: () => DateConverter}) // use custom converter for special type
  public createdAt: Date;
}

As shown in the example above, each configuration field is optional : you can define any field you want or not to have any configuration at all.

| Field | Description | Available on @Id() | |------------------------|-----------------------------------------------------------------------------------------------------------------|------------------------| | field | Field name in JSON | Yes | | type | Field type after deserialization : only fields with @Id() or @Column() decorator in type will be serialized | No | | readOnly | Boolean to indicate to not send the value in json to the server | Yes | | writeOnly | Boolean to indicate to ignore the field in json | Yes | | customConverter | A converter to make a custom serialization/deserialization | No | | denormalizeNull | Boolean to override global configuration to denormalize the column when is set to null value | Yes | | denormalizeUndefined | Boolean to override global configuration to denormalize the column when is set to undefined value | Yes | | normalizeNull | Boolean to override global configuration to normalize the column when is set to null value | Yes | | normalizeUndefined | Boolean to override global configuration to normalize the column when is set to undefined value | Yes | |

The field and type fields can be defined using a shorthand. For field, just specify a string directly in the context, it will be interpreted as such. Same thing for type : specify directly an anonymous function returning the type in the context (as shown in the example).

Path parameters and PathColumn

You can add parameters on a resource path using : character. You can map those parameters with values using the query system.

@PathColumn() decorator allows you to retrieve the path parameter value of a resource and map it to the decorated field.

import { HttpResource } from '@paddls/ngx-http-repository';
import { Id, Column, PathColumn } from '@paddls/ngx-repository';

@HttpResource({
  path: '/libraries/:libraryId/books'
})
export class Book {

  @Id()
  public id: number;

  @Column()
  public title: string;

  @PathColumn()
  public libraryId: string;

  // or

  @PathColumn('libraryId')
  public theLibraryId: string;

}

Fetch associated resources

JoinColumn

You can fetch associated resources using JoinColumn.

import { HttpResource } from '@paddls/ngx-http-repository';
import { Id, Column, JoinColumn } from '@paddls/ngx-repository';
import { FirestoreRepository } from '@paddls/ngx-firestore-repository';

@HttpResource({
  path: '/libraries/:libraryId/books'
})
export class Book {

  @Id()
  public id: number;

  @Column()
  public title: string;

  @Column()
  public authorId: string;

  // initialize the request to get associated author, using instance attribute 'authorId' with User FirestoreRepository
  @JoinColumn({attribute: 'authorId', resourceType: () => User, repository: () => FirestoreRepository})
  public author$: Observable<Person>; // data will be lazy fetched on subscribe
}

SubCollection

You can fetch associated resources using SubCollection.

import { HttpResource, HttpRepository } from '@paddls/ngx-http-repository';
import { Id, Column, SubCollection } from '@paddls/ngx-repository';

@HttpResource({
  path: '/libraries/:libraryId/books'
})
export class Book {

  @Id()
  public id: number;

  @Column()
  public title: string;

  // you should specify the corresponding resource, how to request resources and the repository to use
  @SubCollection({
    resourceType: () => Comment,
    // params are extra information context request (for example libraryId in path) 
    params: (book: Book, params: any) => new CommentQuery({bookId: book.id, libraryId: params.libraryId}),
    repository: () => HttpRepository
  })
  public comments$: Observable<Comment[]>;
}

Query

With NgxRepository, a Query is an object holding some informations associated with the querying of one or several resources. You can then provide an instance of this Query as a parameter of any method available on HttpRepository or FirestoreRepository.

Here is an example of a query for a @HttpResource() :

import { HttpQueryParam, HttpHeader } from '@paddls/ngx-http-repository';
import { PathParam } from '@paddls/ngx-repository';

export class BookQuery {

  // use http query param

  @HttpQueryParam() // param is forwarded into http query param
  public title: string;

  // or

  @HttpQueryParam('title')
  public theTitle: string;

  // use path param

  @PathParam() // param replace :libraryId into resource path
  public libraryId: string;

  // or

  @PathParam('libraryId')
  public library: string;

  // use http header

  @HttpHeader() // path is forwarded into http header
  public page: number = 1;

  // or

  @HttpHeader('itemPerPage')
  public size: number = 2;

  public constructor(data: Partial<BookQuery> = {}) {
    Object.assign(this, data);
  }
}

And an example of a query for a @FirestoreResource() :

import {
  FirestoreCriteria,
  FirestoreEndAt,
  FirestoreEndBefore,
  FirestoreLimit,
  FirestoreLimitToLast,
  FirestoreOrderBy,
  FirestoreOrderByContext,
  FirestoreStartAfter,
  FirestoreStartAt
} from '@paddls/ngx-firestore-repository';

export class ClientQuery {

  @FirestoreCriteria({field: 'lastName', operator: '=='})
  public lastNameEqual?: string;

  @FirestoreOrderBy()
  public orderBy?: string | FirestoreOrderByContext | (FirestoreOrderByContext | string)[];

  @FirestoreStartAt()
  public startAt?: any;

  @FirestoreStartAfter()
  public startAfter?: any;

  @FirestoreEndAt()
  public endAt?: any;

  @FirestoreEndBefore()
  public endBefore?: any;

  @FirestoreLimit()
  public limit?: number;

  @FirestoreLimitToLast()
  public limitToLast?: number;

  public constructor(data: Partial<ClientQuery> = {}) {
    Object.assign(this, data);
  }
}

The following table lists all the type of fields you can add to a query object and with which repository they are available.

| Decorator | Description | Repository type | |---------------------------|-------------------------------------------------------------------|-----------------------------------------| | @PathParam() | Replaces path parameter with field value | HttpRepository, FirestoreRepository | | @HttpQueryParam() | Adds a query param to the HTTP request (eg. /users/?name=Oscar) | HttpRepository | | @HttpHeader() | Adds a HTTP header to the request with field value | HttpRepository | | @FirestoreCriteria() | Adds a Firestore query criteria | FirestoreRepository | | @FirestoreOrderBy() | Adds a .orderBy() clause to Firestore request | FirestoreRepository | | @FirestoreLimit() | Adds a .limit() clause to Firestore request | FirestoreRepository | | @FirestoreLimitToLast() | Adds a .limitToLast() clause to Firestore request | FirestoreRepository | | @FirestoreStartAt() | Adds a .startAt() query cursor to Firestore request | FirestoreRepository | | @FirestoreStartAfter() | Adds a .startAfter() query cursor to Firestore request | FirestoreRepository | | @FirestoreEndAt() | Adds a .endAt() query cursor to Firestore request | FirestoreRepository | | @FirestoreEndBefore() | Adds a .endBefore() query cursor to Firestore request | FirestoreRepository |

The following example shows a query used in a findAll() operation on a Firestore resource.

@Injectable()
export class ClientService {

  @InjectRepository({resourceType: () => Client, repository: () => FirestoreRepository})
  private repository: FirestoreRepository<Client, string>;

  public searchByLastName(searchedLastName: string): Observable<Page<Client>> {
    return this.repository.findAll(new ClientQuery({
      lastNameEqual: searchedLastName,
      orderBy: ['firstName']
    }));
  }
}

Resource configuration

You can configure your resource to your needs by adding context to @HttpResource() and @FirestoreResource() resource decorators.

For example, you may need to specify a different path to your resource depending on the operation. This can be done by using the syntax in the following example.

@HttpResource({
  path: '/libraries',
  update: '/library',
})
export class Library {

}

For each operation execution, NgxRepository will look for a specific context attached to the operation for the resource. If no specific context is found, the default context (here the path) will be used.

The available configuration is different depending on the type of resource and the targeted operation.

The targetable operations are the following :

  • findById
  • findOne
  • findAll
  • create
  • update
  • patch
  • delete
  • read
  • write

Response type

Define a specific response type (different from the resource) using responseType context parameter.

@HttpResource({
  path: '/libraries',
  write: {
    responseType: () => WriteLibraryDto
  }
})
export class Library {

}

Path

Define a specific path for an operation using path context parameter. Passing a string directly as an operation context is also possible to define the path context parameter.

⚠️ This context parameter is only available for @HttpResource()

@HttpResource({
  path: '/libraries',
  create: {
    path: '/create-a-library'
  },
  update: '/update-a-library'
})
export class Library {

}

Page response processor

Define a specific page response processor using pageResponseProcessor context parameter.

⚠️ This context parameter is only available for findAll operation and @HttpResource()

@HttpResource({
  path: '/libraries',
  findAll: {
    pageResponseProcessor: MyPageResponseProcessor
  }
})
export class Library {

}

Global configuration

It is also possible to add some global configuration by adding it directly in the forRoot() method of your driver module import.

HTTP Pagination

⚠️ This feature is only available for @HttpResource()

Page type

NgxRepository comes with a prebuilt HTTP pagination system. If a @HttpResource() is paginated server-side, the server usually sends in the HTTP response some context about the data being sent (page number, total number of elements and page size). NgxRepository holds these infos in a Page object.

The Page class extends the Array type and exposes additional infos about eventual server-side pagination.

This is the implementation of the Page type :

export class Page<T = any> extends Array<T> {

  public currentPage: number;

  public itemsPerPage: number;

  public totalItems: number;
}

The findAll method of HttpRepository returns a Observable<Page<T>> where T is your resource. You can use it as an Array or as a Page if you want to access the pagination infos.

Page response processor

By default, the fields of the Page object returned by the findAllmethod of HttpRepository will be equal to :

  • 0 for the currentPage field
  • items.length for the itemsPerPage and totalItems fields

This is because the pagination infos can be returned in different ways (sometimes in the response headers, sometimes in the body... ). To indicate how the server returns these elements, you have to create a PageResponseProcessor.

import { Injectable } from '@angular/core';
import { Page, RequestManagerContext, ResponseProcessor } from '@paddls/ngx-repository';
import { HttpRepositoryResponse } from '@paddls/ngx-http-repository';

@Injectable()
export class MyPageResponseProcessor implements ResponseProcessor {

  public transform(response: any, origin: HttpRepositoryResponse, context: RequestManagerContext): any {
    const totalItems: number = parseInt(origin.getHeaders().get('apiTotalItems'), 10);
    const itemsPerPage: number = parseInt(origin.getHeaders().get('apiPerPage'), 10);
    const currentPage: number = parseInt(origin.getHeaders().get('apiCurrentPage'), 10);

    return Page.build(response, currentPage, itemsPerPage, totalItems);
  }
}

A PageResponseProcessor implements the ResponseProcessor interface. This interface exposes a transform() method which transforms the HTTP response. In this method, you can build a Page object using the infos about the HTTP response provided in the transform() method parameters.

Then, provide this processor in your AppModule and add it to any findAll configuration you need. It can be on a specific HttpResource() or globally.

HttpLiveResource

⚠️ This feature is only available for @HttpResource()

If you want to build a truly reactive app, you may need the data displayed in your app to always be in sync with server's data. This can be achieved by sending a new GET request each time a POST, PUT, PATCH or DELETE request is made.

By annotating your model with @HttpLiveResource() decorator, each observable returned by findAll() , findOne() or findById() method will be transformed into a hot observable that will emit a new value each time a writing method is called on the repository.

import { HttpLiveResource, HttpResource } from '@paddls/ngx-http-repository';

@HttpLiveResource()
@HttpResource('/libraries')
export class Library {
}

Multipart

⚠️ This feature is only available for @HttpResource()

To enable multipart, you should specify multipartfield from write, create, update, patch, delete. Then you can specified each part on field using @HttpMultipartColumn decorator.

import { HttpMultipartColumn, HttpResource } from '@paddls/ngx-http-repository';

@HttpResource({
    path: '/libraries',
    write: {
      multipart: 'partNameOfLibrary',
    }
})
export class Library {

    @HttpMultipartColumn('partNameOfFile')
    public file: File
}

FirestoreCreatedAt and FirestoreUpdatedAt

Use @FirestoreCreatedAt() and @FirestoreUpdatedAt() decorators on a @FirestoreResource() field to automatically inject creation and last update date of each resource firestore document.

You can also retrieve information for a particular resource field by adding its property name directly in the decorator context.

@FirestoreResource({
  path: '/clients'
})
export class Client {

  @Id()
  public id: string;

  @Column()
  public name: string;

  @FirestoreCreatedAt()
  public createdAt: Date;

  @FirestoreUpdatedAt('name')
  public updatedAt: Date;

  public constructor(data: Partial<Client> = {}) {
    Object.assign(this, data);
  }
}

Advanced usage

Custom Repository

In specific case, you can define a custom repository like that :

import { Repository } from '@paddls/ng-repository';
import { HttpRepository, HTTP_REPOSITORY_CONFIGURATION } from '@paddls/ng-http-repository';

@Injectable()
@Repository(() => Person)
export class PersonRepository extends HttpRepository<Person, string> {

  // use global configuration
  public constructor(requestManager: RequestManager,
                     driver: HttpRepositoryDriver,
                     @Inject(HTTP_REPOSITORY_CONFIGURATION)
                       configuration: ResourceConfiguration) {
    super(requestManager, driver, configuration);
  }

  // overide global configuration
  public constructor(requestManager: RequestManager,
                     driver: HttpRepositoryDriver) {
    super(requestManager, driver, {
      // ... my configuration here
    });
  }

  public searchByFirstName(searchedFirstName: string): Observable<Person[]> {
    // write your custom logic here
  }
}

@Injectable()
export class PersonService {

  // Like a standard service, you can inject your custom repository
  public constructor(private readonly personRepository: PersonRepository) {
  }
}

Custom HTTP API

Sometimes, you may use an HTTP API that does not fit REST standards. For example, you may want to send GET requests with bodies or requests with a response type different from body type.

To do that, simply create an @Injectable() class and define your API methods inside like in the following example :

@Injectable()
class Api {

  @HttpGet('/api', () => ApiResponse)
  public get: HttpQueryFn<ApiResponse>;

  @HttpGet({
    path: '/api',
    withBody: true
  }, () => ApiResponse)
  public getWithBody: HttpBodyFn<ApiResponse>;

  @HttpGet({
    path: '/api',
    postResponseProcessors: PageResponseProcessor
  }, () => ApiResponse)
  public getWithResponseProcessor: HttpQueryFn<Page<ApiResponse>>;

  @HttpGet('/api/:version', () => ApiResponse)
  public getWithPathParam: HttpQueryFn<ApiResponse>;

  @HttpPost('/api', () => ApiResponse)
  public post: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPost({
    path: '/api',
    postResponseProcessors: [PageResponseProcessor]
  }, () => ApiResponse)
  public postWithResponseProcessor: HttpBodyFn<Page<ApiResponse>>;

  @HttpPost({
    path: '/api',
    withBody: false
  }, () => ApiResponse)
  public postWithoutBody: HttpQueryFn<Page<ApiResponse>>;

  @HttpPost('/api/:version', () => ApiResponse)
  public postWithPathParam: HttpBodyFn<ApiResponse>;

  @HttpPut('/api', () => ApiResponse)
  public put: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPut('/api/:version', () => ApiResponse)
  public putWithPathParam: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPut({
    path: '/api',
    withBody: false
  }, () => ApiResponse)
  public putWithoutBody: HttpQueryFn<ApiResponse, ApiRequest>;

  @HttpPut({
    path: '/api',
    postResponseProcessors: [PageResponseProcessor]
  }, () => ApiResponse)
  public putWithResponseProcessor: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPatch('/api', () => ApiResponse)
  public patch: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPatch('/api/:version', () => ApiResponse)
  public patchWithPathParam: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpPatch({
    path: '/api',
    withBody: false
  }, () => ApiResponse)
  public patchWithoutBody: HttpQueryFn<ApiResponse, ApiRequest>;

  @HttpPatch({
    path: '/api',
    postResponseProcessors: [PageResponseProcessor]
  }, () => ApiResponse)
  public patchWithResponseProcessor: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpDelete('/api', () => ApiResponse)
  public delete: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpDelete('/api/:version', () => ApiResponse)
  public deleteWithPathParam: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpDelete({
    path: '/api',
    withBody: false
  }, () => ApiResponse)
  public deleteWithoutBody: HttpQueryFn<ApiResponse, ApiRequest>;

  @HttpDelete({
    path: '/api',
    postResponseProcessors: [PageResponseProcessor]
  }, () => ApiResponse)
  public deleteWithResponseProcessor: HttpBodyFn<ApiResponse, ApiRequest>;

  @HttpOption('/api', () => ApiResponse)
  public option: HttpQueryFn<ApiResponse>;

  @HttpOption('/api/:version', () => ApiResponse)
  public optionWithPathParam: HttpQueryFn<ApiResponse>;

  @HttpOption({
    path: '/api',
    withBody: true
  }, () => ApiResponse)
  public optionWithBody: HttpBodyFn<ApiResponse>;

  @HttpOption({
    path: '/api',
    postResponseProcessors: [PageResponseProcessor]
  }, () => ApiResponse)
  public optionWithResponseProcessor: HttpQueryFn<ApiResponse>;

}

In this example, ApiRequest and ApiResponse are typescript classes with an @Id() field and Column() fields :

  class ApiResponse {

  @Id()
  public id: number;

  @Column()
  public column: string;

  @PathColumn()
  public version: string;

  public constructor(data: Partial<ApiResponse> = {}) {
    Object.assign(this, data);
  }
}

class ApiRequest {

  @Id()
  public identifier: number;

  @Column()
  public name: string;

  public constructor(data: Partial<ApiRequest> = {}) {
    Object.assign(this, data);
  }
}

Then, inject your Api like any other service and make requests by calling the previously defined methods.

Event system

NgxRepository comes with a custom event system. Events are published during a request lifecycle, and you can use event listeners to add custom logic when these events are published.

@Injectable()
@EventListener()
export class LogEventListener implements Listener<any> {

  public on(event: any): void {
    console.log(event);
  }
}

An event listener is a class decorated with @EventListener. You can pass a predicate or an array of predicates to the decorator to filter the events you want to listen to.

Here is the list of all events produced in NgxRepository :

| Event | Repository type | |--------------------------------------|-----------------------| | BeforeExecuteHttpRequestEvent | HttpRepository | | BeforeHttpCreateEvent | HttpRepository | | BeforeHttpDeleteEvent | HttpRepository | | BeforeHttpFindAllEvent | HttpRepository | | BeforeHttpFindByIdEvent | HttpRepository | | BeforeHttpFindOneEvent | HttpRepository | | BeforeHttpPatchEvent | HttpRepository | | BeforeHttpUpdateEvent | HttpRepository | | AfterHttpCreateEvent | HttpRepository | | AfterHttpDeleteEvent | HttpRepository | | AfterHttpFindAllEvent | HttpRepository | | AfterHttpFindByIdEvent | HttpRepository | | AfterHttpFindOneEvent | HttpRepository | | AfterHttpPatchEvent | HttpRepository | | AfterHttpUpdateEvent | HttpRepository | | AfterExecuteHttpRequestEvent | HttpRepository | | BeforeExecuteFirestoreRequestEvent | FirestoreRepository | | BeforeFirestoreCreateEvent | FirestoreRepository | | BeforeFirestoreDeleteEvent | FirestoreRepository | | BeforeFirestoreFindAllEvent | FirestoreRepository | | BeforeFirestoreFindByIdEvent | FirestoreRepository | | BeforeFirestoreFindOneEvent | FirestoreRepository | | BeforeFirestorePatchEvent | FirestoreRepository | | BeforeFirestoreUpdateEvent | FirestoreRepository | | AfterFirestoreCreateEvent | FirestoreRepository | | AfterFirestoreDeleteEvent | FirestoreRepository | | AfterFirestoreFindAllEvent | FirestoreRepository | | AfterFirestoreFindByIdEvent | FirestoreRepository | | AfterFirestoreFindOneEvent | FirestoreRepository | | AfterFirestorePatchEvent | FirestoreRepository | | AfterFirestoreUpdateEvent | FirestoreRepository | | AfterExecuteFirestoreRequestEvent | FirestoreRepository |

Test and debug

NgxRepositoryTestingModule

You can easily test any service using NgxRepository using NgxRepositoryTestingModule like in the following example :

import { TestBed } from '@angular/core/testing';
import { MockRepository, NgxRepositoryTestingModule, Page } from '@paddls/ngx-repository';
import { LibraryService } from './library.service';
import { Library } from '../model/library.model';
import { HttpRepository } from '@paddls/ngx-http-repository';
import { LibraryQuery } from '../query/library.query';

describe('LibraryService', () => {
  let libraryService: LibraryService;
  let libraryRepository: MockRepository;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        NgxRepositoryTestingModule.forTest()
      ],
      providers: [
        LibraryService
      ]
    });

    libraryRepository = NgxRepositoryTestingModule.getRepository(Library, HttpRepository);
    libraryService = TestBed.get(LibraryService);
  });

  describe('#findAll', () => {
    it('should call findAll from read repository', (done: DoneFn) => {
      spyOn(libraryRepository, 'findAll').and.callThrough();

      libraryService.findAll(1, 5).subscribe((page: Page) => {
        expect(libraryRepository.findAll).toHaveBeenCalledWith(new LibraryQuery({
          opened: true,
          page: 1,
          itemPerPage: 5
        }));

        expect(page.length).toBe(0);

        done();
      });

      libraryRepository.emit('findAll', Page.build([]));
      expect(libraryRepository.findAll).toHaveBeenCalledWith(new LibraryQuery({
        opened: true,
        page: 1,
        itemPerPage: 5
      }));
    });
  });
});

Enable debug mode

You can enable debug mode by setting debug flag to true in your repository module import config.

@NgModule({
  imports: [
    BrowserModule,
    CoreModule,
    NgxRepositoryModule.forRoot(),
    NgxFirestoreRepositoryModule.forRoot({
      debug: true
    }),
    NgxHttpRepositoryModule.forRoot({
      debug: true
    }),
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Then, open your console to see debug messages for each request made.

Install and build project

To install and build the project, you just have to clone the repository and make the dependency installation :

npm i

After dependency installation, you can run others commands :

# Run the tests
npm run test

# Run the linter
npm run lint

# Run the example app
npm run start

Release

You should release from the master branch

# create the release
npm run release --release=x.x.x

Then push