Multiple collections in Angular-in-memory-web-api

How do we create multiple collections using Angular-in-memory-web-api? Not an issue with single collection. But I'm not able to implement it for multiple collections.

For example, I want to create say two collections in the memory db - Country and Cities. Any idea, how to do that?


Solution 1:

Just return it an object with both arrays. In the example from Angular, you see something like

createDb() {
  let heroes = [ .. ]
  return { heroes }
}

If you don't already know this, { heroes } is just shorthand for writing { heroes: heroes }. So if you have two collections, then just add it as a another property

createDb() {
  let heroes = [ .. ];
  let crises = [ .. ];
  return { heroes, crises };
  // or { heroes: heroes, crises: crises }
}

The name of the property returned will be used for the path in the URL. So you can use

/api/heroes/1
/api/crises/1

Solution 2:

The approach described in the Paul's answer is correct, however there is one detail I missed which I like to add: How do you specify genId correctly, so it works for both collections?

The answer is referring to the "Heroes" example written in TypeScript (a superset of JavaScript), specifically the HTTP chapter. There, a table heroes is simulated by implementing:

export class InMemoryDataService implements InMemoryDbService {
  createDb() {
    const heroes = [
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      // ...
      { id: 20, name: 'Tornado' }
    ];
    return {heroes};
  }

  // Overrides the genId method to ensure that a hero always has an id.
  // If the heroes array is empty,
  // the method below returns the initial number (11).
  // if the heroes array is not empty, the method below returns the highest
  // hero id + 1.
  genId(heroes: Hero[]): number {
    return heroes.length > 0 ? Math.max(...heroes.map(hero => hero.id)) + 1 : 11;
  }
}

Now, if you add a 2nd collection crises as shown in his answer, i.e.:

createDb() {
  let heroes = [ { ... }, ... ];
  let crises = [ { ... }, ... ];
  return { heroes, crises };
  // or { heroes: heroes, crises: crises }
}

how do you provide genId for both collections (provided they are of type Hero and Crises)? Overloading, as you would do it in C# does not work in TypeScript, it would throw an error ("Duplicate function implementation").


Solution: I found out, that you can solve this issue with TypeScript's Generics as follows. Replace the otiginal genId function by the following generic version:

genId<T extends Hero | Crises>(myTable: T[]): number {
  return myTable.length > 0 ? Math.max(...myTable.map(t => t.id)) + 1 : 11;
}

What's important here is the part <T extends Hero | Crises>: It means that type T can be either Hero or Crises: So it will be invoked if the parameter passed is either of type Hero[] or Crises[].

With that knowledge, adding a 3rd, 4th, ... class is simple: Just append the class. Say we want to add the class SuperHero, then you just append it via | SuperHero, so it would look like:

genId<T extends Hero | Crises | SuperHero>(myTable: T[]): number {
  return myTable.length > 0 ? Math.max(...myTable.map(t => t.id)) + 1 : 11;
}

Note: As a prerequisite, all classes (Hero, Crises and SuperHero) need to have a numeric id property declared.


Useful links:

  • HTTP chapter of Angular Tutorial

  • In-Memory-Web-API: Custom genId

  • Can You Specify Multiple Type Constraints For TypeScript Generics?