5
\$\begingroup\$

I have an API which I can't modify. I have to use data as it is.

I want to join data from two separate requests where the second request is based on data from the first one.

I have a working solution already but the code quality, in my opinion, is low so I want to know your opinion (and if you can provide: better, more reactive solution).

So, I have an order API that's receiving data:

/orders response:

[
  {
    id: 1,
    clientId: 201,
    // other data
  },
  {
    id: 2,
    clientId: 201,
  },
  {
    id: 3,
    clientId: 11,
  }
]

In this response, I don't have data about the client name, so I have a second API where I can pass IDs of clients and then I got back:

/clientsData?ids=201,11 response:

[
  {
    id:201,
    name: "Jon Doe"
  },
  {
    id: 11,
    name: "Jack Sparrow"
  }
]

So, now I have a working code but as mentioned, in the beginning, I'm feeling it can be done in a better way. Please, note that the request should be done in the correct order because I need clients IDs before I make a second API call.

Also, want to avoid making multiple (more than two [orders+clients]) requests to check client data.

Working code:


// API Call to get orders:
const result$ = this.http.get('/orders').pipe(
  mergeMap(
    orders => {
      const ids = orders
        .map(item => item.clientId)
        .filter((v, i, a) => a.indexOf(v) === i) // remove duplicates
        .join(','); // get client ids into one string

      // Client IDs: 1,2,99,201
      // API call to check IDs:
      return this.http.get(`/clientsData?ids=${ids}`).pipe(
        mergeMap(clientsData => {
          // map over each client
          clientsData.forEach(client => {
            // then map over each order
            orders.forEach(order => {
              // and assign client name to order
              if (order.clientId === client.id) {
                order.clientName = client.name;
              }
            });
          });

          return of(orders); // observable
        })
      );
    }
  )
);

result$.subscribe(console.log);

I expect to get back Observable with merged data:

[
  {
    id: 1,
    clientId: 201,
    name: "Jon Doe"
    // other data
  },
  {
    id: 2,
    clientId: 201,
    name: "Jon Doe"
  },
  {
    id: 3,
    clientId: 11,
    name: "Jack Sparrow"
  }
]

I want to avoid nested subscriptions and achieve the result in a reactive way.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Looks good. Yeah, sometimes APIs are hard to work with.

Instead of the loop clientsData.forEach, you might want to reduce the client data down to an object, where the keys are the client id (ie. index on the id). Then, map through your orders, and use this object to add in the client name. This will be O(N) vs. O(N^2)

I'd also probably use a different object for orders vs. the orders with the name... just to avoid mutation, which I didn't expect... This is obviously more a style question, though. I favor immutable code, especially when it's more functional like this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.