Combing data from multiple API endpoints with NgRX in selectors or effects?

52 views Asked by At

I'm starting to learn Angular and NgRx, and I'm trying to understand the correct way to use NgRx to combine data from multiple API endpoints that reference each other with ids.

Say I have an API with endpoints that returns products and categories with the following formats.

//GET /products/{product_id}
//GET /products/
export interface Product {
  name: string;
  category_id: number;
}

//GET /categories/
export interface Category {
  id: number;
  name: string;
}

Then I add this to my AppState as follows:

export interface AppState {
  product: Product;
  categories: Category[];
}

In the frontend I want to display the category name instead of the id when displaying a Product. So I can can compose selectors to return a ProductView that combines the Category with a Product like this:

export interface ProductView {
  name: string;
  category?: Category;
  related_products: ProductView[];
}

let productSelector = (appState: AppState) => {
  return appState.product;
};

let categoriesSelector = (appState: AppState) => {
  return appState.categories;
};


let productViewSelector = createSelector(
  productSelector,
  categoriesSelector,
  (product, categories) => {
    let productView: ProductView = {
      name: product.name,
      category: categories.find((c) => c.id == product.category_id),
    };
    return productView;
  }
);

Now say I have LoadCategories and LoadCategoriesSuccess actions with corresponding effect and reducers to populate AppState.categories from the backend. How/where should I be dispatching the LoadCategories action?

Selectors and reducers are suppose to be pure functions. So I should not be dispatching actions from them correct?

I could dispatch LoadCategories when the application starts using APP_INITIALIZER. However, this will only run once. If a new category is added to the backend while the application is open it wont show up on an associated product. So it seems I would want logic to load category(ies) when a project is loaded.

I could put logic in an effect that handles the LoadProjectSuccess action that checks if categoriesSelector currently has a category with an id that matches the current project being loaded. If the category_id of the project is not known it would then dispatch LoadCategories to load the latest list of categories. Is this the right way to handle something like this?

Alternatively, since I only really care about the ProductView in my Angular components I could simplify AppState to only include ProductView.

export interface AppState {
  product: ProductView;
}

Then I would handle making multiple API calls and merging the results into a ProductView in an effect rather then the selector. This seems possible, but to be frank I'm pretty confused with all the rxjs selectors like mergeMap, switchMap, getLatestFrom, forkJoin etc... So I'm not entirely sure the correct way to do this or if this is the right approach.

0

There are 0 answers