0

Let's say we have an api enpoint to deal with a superhero resource:

  • api/superheroes POST - to create a hero
  • api/superheroes/{id} GET - to fetch a hero

The resource model is the following:

public class SuperHero
{
    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }
}

Now a Product Owners say they'd like to introduce superhero teams. As of now a given team has a name and at least one hero attached. I can see two options to proceed with.

1) Straightforward

Introduce a new resource:

api/teams POST - creates a team

And the resource model will look like the following:

public class Team
{
    public int Id { get; set; }

    public string Name { get; set; }

    public IEnumerable<int> SuperHeroes { get; set; }
}

In such a way api users will send a single request to create a team. A list of superheroes must be specified and be non-empty

2) Revisit requirements

What does

team has a name and at least one hero attached

actually mean? Well, having no heroes the team is definetely not ready to save the world. But is it the obstacle to its existence? And do we really need to prevent users from creating a team with no heroes attached?

Let's introduce a team and attached superheroes as a resource and sub-resource respenctively:

  • api/teams POST - to create a team with a name and an empty list of members

    public class Team
    {
        public string Id { get; set; }
    
        public string Name { get; set; }    
    
        //potentially other team properties   
    }
    
  • api/teams/superheroes POST - to attach heroes to a team

    public class TeamSuperhero
    {
        public string SuperheroId { get; set; }
    
        //potentially other properties related to the fact of attach itself
    }
    

My feeling is that the second approach contributes to endpoints' simplicity and responsibility separation. Also it might be better from performance perspective as fetching a team probably doesn't mean the user needs an associated superhero list. At the same time it forces api consumers to use two calls instead of a single one to create a ready-to-go team.

What additional questions should be asked here to choose the right option?

Justin
  • 84,773
  • 49
  • 224
  • 367
Artem
  • 2,084
  • 2
  • 21
  • 29
  • Please let me know if my [answer](https://stackoverflow.com/a/44095846/1426227) is useful for you. – cassiomolin May 23 '17 at 23:14
  • So, you suggest to expose both possibilities to the end user, right? Makes sense. However how GET team resource model should look like? Should it always contain list of heroes? Won't it be a performance gap here (under the curtain something like joining to team_hero_connection table for example) – Artem Jun 01 '17 at 07:05
  • 1
    I think managing the relationship from both sides is a personal choice. It depends on your requirements. To lazy load a collection, have a look [here](https://stackoverflow.com/a/42147688/1426227) and [here](https://stackoverflow.com/a/37457166/1426227). – cassiomolin Jun 01 '17 at 08:31

1 Answers1

1

Creating a team

When creating a team, you could allow the consumer to add heroes to it:

POST /api/teams HTTP/1.1
Host: example.org
Content-Type: application/json

{
  "name": "justice-league",
  "heroes": [
    "batman",
    "superman",
    "wonder-woman"
  ]
}

Attaching a hero to a team

To add heroes to a team, you could:

POST /api/teams/avengers/heroes HTTP/1.1
Host: example.org
Content-Type: application/json

{
  "value": [
    "captain-america",
    "iron-man"
  ]
}

Where you semantically append members to a collection.

To replace the heroes of a team:

PUT /api/teams/avengers/heroes HTTP/1.1
Host: example.org
Content-Type: application/json

{
  "value": [
    "captain-america",
    "iron-man",
    "hulk",
    "thor"
  ]
}

You also could consider:

PUT /api/heroes/harley-quinn/team HTTP/1.1
Host: example.org
Content-Type: application/json

{
  "value": "suicide-squad"
}

Where you semantically replace the team that the hero belongs to.

Detaching a hero from a team

To remove a hero from a team you could:

DELETE /api/teams/avengers/heroes/iron-man HTTP/1.1
Host: example.org

Or even:

DELETE /api/heroes/iron-man/team HTTP/1.1
Host: example.org

Bear in mind that your persistence model doesn't need to be like your API model.

cassiomolin
  • 124,154
  • 35
  • 280
  • 359