Query module based on the @ngrx/store package.
yarn add @ngsm/query or npm i @ngsm/query --save
Library requires @ngrx/store module.
State (for example homepage.state.ts):
import { queryReducer } from '@ngsm/query';
import { HomepageApiResponseDto } from 'your-api-dto.interfaces.ts';
export interface HomepageQueryState {
  getHomepageApiQuery?: Query<HomepageApiResponseDto>;
}
export const HOMEPAGE_QUERY_KEY = 'homepageQuery';
export interface HomepagePartialState {
  readonly [HOMEPAGE_QUERY_KEY]: HomepageQueryState;
  // Your feature states, for example:
  // readonly [HOMEPAGE_FEATURE_KEY]: HomepageState;
}Reducer (for example homepage.reducer.ts):
import { Action } from '@ngrx/store';
import { HomepageQueryState } from './homepage.state';
...
export function homepageQueryReducer(state: HomepageQueryState | undefined, action: Action) {
  return queryReducer(state, action);
}Selectors (for example homepage.selectors.ts):
import { createFeatureSelector, createSelector } from '@ngrx/store';
export const homepageQueryState = createFeatureSelector<HomepagePartialState, HomepageQueryState>(HOMEPAGE_QUERY_KEY);
export const getHomepageApiQuery = createSelector(
  homepageQueryState,
  (state: HomepageQueryState) => state.getHomepageApiQuery
);State module (for example homepage-state.module.ts):
import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { HomapageEffects } from './homepage.effects';
import { HomapageFacade } from './homepage.facade';
import { homepageQueryReducer, homepageReducer } from './homepage.reducer';
import { HOMEPAGE_QUERY_KEY } from './homepage.state';
@NgModule({
  imports: [
    StoreModule.forFeature(HOMEPAGE_QUERY_KEY, homepageQueryReducer),
    EffectsModule.forFeature([HomapageEffects]),
  ],
  providers: [HomapageFacade]
})
export class HomapageStateModule {}Effects (for example homepage.effects.ts):
  ...
  getHomepageApi$ = createEffect(() =>
    this.actions$.pipe(
      ofType(HomepageActions.getHomepageApi),
      mergeMap(() => concat(
        // run inProgress action
        of(QueryActions.inProgress({ query: HomepageQuery.getHomepageApiQuery })),
        this.homepageRepository
          .getHomepageApi()
          .pipe(
            mergeMap((response) => [
              // run success action
              QueryActions.success({ query: HomepageQuery.getHomepageApiQuery, response }),
            ]),
            catchError(error => [
              // run failure action
              QueryActions.failure({ query: HomepageQuery.getHomepageApiQuery, error }),
            ])
          )
      ))
    )
  );
  ...Facade (for example homepage.facade.ts):
...
@Injectable()
export class HomepageFacade {
  getHomepageQuery$ = this.store.pipe(select(HomepageSelectors.getHomepageQuery));
  loader$ = isQueryInProgress$([
    this.getHomepageQuery$,
    // add all feature queries
    ...,
  ]);
  constructor(private store: Store<HomepagePartialState>) {}
  dispatch(action: Action) {
    this.store.dispatch(action);
  }
}|   | Sebastian Musiał |