Best practices for implementing many-to-many associations in REST APIs

I’m working on a REST API design and struggling with how to properly handle many-to-many relationships. My scenario involves Author and Book entities where authors can write multiple books and books can have multiple authors. I have separate tables for each entity plus a junction table for the relationships. I’m considering two main approaches:

Option 1: Embed related entities directly
Each entity includes references to related ones. So an Author would contain a list of Books, and each Book would contain a list of Authors.

/api/author/1:
{
    "id": 1,
    "fullName": "Stephen King",
    "birthDate": "1947-09-21",
    "books": [
        "/api/book/101",
        "/api/book/102",
        "/api/book/103"
    ]
}

/api/book/101:
{
    "id": 101,
    "title": "The Shining",
    "publishedYear": 1977,
    "authors": [
        "/api/author/1"
    ]
}

Option 2: Treat the relationship as its own resource
Create separate endpoints for managing the associations themselves.

/api/author/1:
{
    "id": 1,
    "fullName": "Stephen King",
    "birthDate": "1947-09-21"
}

/api/book/101:
{
    "id": 101,
    "title": "The Shining",
    "publishedYear": 1977
}

/api/author/1/books:
[
    "/api/book/101",
    "/api/book/102",
    "/api/book/103"
]

Which approach follows REST principles better? Are there other patterns I should consider?

i think option 1 is less complcated for devs, especially if you’re just starting out. it kinda keeps things neat and reduces calls. option 2 is good if you need to handle those links somehow or get extra data on them, but can be overkill.

I’ve dealt with this before in production APIs. Your choice depends on query patterns and performance needs. Option 1 can kill performance when authors have hundreds of books - I’ve seen responses hit several megabytes from embedded collections. Lazy loading with pagination works way better. /api/author/1 returns basic author info plus a books_href pointing to /api/author/1/books?page=1&size=20. You get discoverability without the performance hit. Also throw in batch endpoints like /api/authors/bulk for multiple IDs if clients need efficient related data fetching. Focus on how your frontend actually uses the data instead of blindly following REST theory.

I’ve built APIs with similar relationships, and I’d go with a hybrid approach. For reads, Option 1 works great when you need the relationship data right away - just add an expansion parameter like /api/author/1?expand=books so clients can control what gets loaded without making multiple requests. For writes, treat relationships as separate resources (Option 2). POST to /api/author/1/books to add a book relationship, DELETE to remove it. This keeps things predictable and cacheable. Just watch out for circular references in your JSON responses when embedding related entities - learned that one the hard way when my serializer got stuck in infinite loops.