Search code examples
mysqlsqlangulartypescriptmany-to-many

Parsing many to many model


I have a book database looking like this (simplified):

[book]
- bookId
- title
[author]
- authorId
- name
[book_has_author]
- bookId
- authorId

So basically a book can have multiple authors and an author can have multiples books.

I created an express API that retrieve me a list of books with his authors with an inner join. Here is what it returns for one book having 2 authors:

[
  {
    bookId: 1,
    title: "how to code",
    authorId: 1,
    name: "martin"
  },
  {
    bookId: 1,
    title: "how to code",
    authorId: 2,
    name: "Tim"
  }
]

But my Angular models looks like this:

interface Author {
  authorId: number,
  name: string,
  books: Book[]
}
interface Book {
  bookId: number,
  title: string,
  authors: Author[]
}

I know it's a bad way to represent my database but I don't know how I can model it.


Solution

  • The main issue here is that interfaces have circular referencies to each other, although I imagine you already figured that out. This is generally a problem and although it's technically correct to think about the data in those terms it's not a good approach to model your data.

    The first solution would be to make a choice between having an author have-books or a book have-authors, thus removing one of the dependencies out of the circular reference. You could also create BookChild and AuthorChild interfaces while keeping the Book and Author ones, but that will end up adding unnecessary complexities if the use case isn't really worth it.

    After making your choice, you would probably need to add some type safety in your query results. To do that you can create a BookAuthorResponse interface and then transform it to a Book through map and reduce operations or the imperative equivalent (for-loop).

    interface Author {
      authorId: number,
      name: string
    }
    
    interface Book {
      bookId: number,
      title: string,
      authors: Author[]
    }
    
    interface BookAuthorResponse {
      bookId: number
      title: string
      authorId: number
      name: string
    }
    
    const bookAuthorResponseArray: BookAuthorResponse[] = [
      {
        bookId: 1,
        title: "how to code",
        authorId: 1,
        name: "martin"
      },
      {
        bookId: 1,
        title: "how to code",
        authorId: 2,
        name: "Tim"
      }
    ]
    
    const books: Book = bookAuthorResponseArray.map(ba => ({
        bookId: ba.bookId,
        title: ba.title,
        authors: [{
          authorId: ba.authorId,
          name: ba.name
        }]
      }))
      .reduce((ba, acc) => ({
        bookId: ba.bookId,
        title: ba.title,
        authors: [...acc.authors, ba.authors[0]]
      }))