4

I try to find the best practice in order to create a Many-to-Many relationship in a Restful API. The usecase is really simple, but i cannot really found the "right" way to do it.

In our model, we have Kid that are related with a Many-to-Many relationships to Guardian. In the relationship table, we have 2 extra parameters, type (parent, nanny, emergency, etc.) and active (Boolean).

You can only add Guardian to a existing Kid, but an existing Guardian can be link with another Kid.

Today, we do it like that

POST kids/{kidID}/guardians
{
    "type": "parent"
    "active": false 
    "guardian": {
        "first_name": "foo"
        "last_name": "bar"
    }
}

This create the Guardian and add it to the Kid. But, with this method we cannot handle the case where I want to add an existing Guardian to a Kid. Here the answers I found in order to represent this, but I don't know which one is the best (and restful) way (maybe none of them are good...) :

Solution 1 - Keep the endpoint as today

But put an non-mandatory id field to the guardian. If id is empty, the API have to create the ressource otherwise is just retrieve it and update values if need.

POST kids/{kidID}/guardians/
{
    "type": "parent"
    "active": false 
    "guardian": {
        "id": "ab65f263-dd3d-bbc6-8b7b-57a3b4b26c21"
    }
}

Solution 2 - Break this endpoint in 2 calls

# Create the Guardian
POST guardians/
{
    "first_name": "foo"
    "last_name": "bar"
}

# This method can only "link" the models
POST kids/{kidID}/guardians/
{
    "type": "parent"
    "active": false 
    "guardian_id": "ab65f263-dd3d-bbc6-8b7b-57a3b4b26c21"
}

[Edited] Solution 2.5 - Create the relationship with a PUT

As before, you have to create the guardian, but in order to add the relationship you make a

PUT kids/{kidID}/guardians/{guardianID}
{
    "type": "parent"
    "active": false 
}

Subsidiary solution : In the second choice we can change the URI of the ressource by:

POST kids/{kidID}/kid-guardians/

Because it doesn't really POST a "guardian" ressource, but a kid-guardian ressource (the relationship). I don't really like it because with the old URI we can assume more easily that

GET kids/{kidID}/guardians/

Will give you all Guardians related with the Kid, but not that

DELETE kids/{kidID}/guardians/{guardianID}

Will delete the relationship and not the Guardian.

So as you understand, i'm really lost and would appreciate your help.

Best regards,

slaynerM
  • 45
  • 2
  • 7

2 Answers2

2

Would not be possible to create a third category of resource for the relationship itself, such as "guard", not subordinated to an instance of the other resources? It seems to be the recommended and usual way to deal with n to n relationships in databases.

GET /guards?kid="Johnny" would give you a list of relationships which you could use to get all its guardians from. GET /guards?guard="Kelly", you can guess it. /kids and /guards would keep the data about the resources themselves only and would probably be easier to maintain than if you have to keep relationship data as part of them.

I think that you could get this more RESTful by using links to each member of the relationship instead of numerical IDs. And you can have a field like "relationships" in the kids and guardians representations with the URL+querystring needed to retrieve their specific "guards" could somebody need them.

  • Thank you for your answer. I didn't keep this approch for several reason : **1 -** It's sound for me more like a database thinking than REST. **2 -** Is the **guards** EP is really a ressource ? All CRUD doesn't seems useful for me, Will i really need GET guards/{guardID} ? **3 -** Can we not use, your EP **and** kids/{kidID}/guardians/ in order to retrieve the relationship. Thank you again for your help, maybe is the best solution and i'm totally wrong about my RESTful point of view – slaynerM Nov 02 '16 at 16:33
  • **1** - I am no expert in neither DB or REST, but it looks like a matter of _modelling__ here, and if the relationship is something important to the business model, it is a potential resource in the API indeed. **2** - Should you want to GET data about a specific relationship, and such relationships can come and go in time, `GET/POST/PUT/DELETE /guards/{id}` makes sense, seems easier to maintain and it can even be hidden in the client app, behind your #3 idea (next). **3** - You could create an alias like that, sure, and equally `/guardians/{id}/kids`. BTW I don't know what "EP" means :) Luck! – Fabricio Rocha Nov 03 '16 at 01:26
0

I'll go with Fabricio Rocha answer, implemented like that :

POST guardian-kids/
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}"
    },
    "kid":{
        "id": "{kidId}"
    }
}

If you want to retrieve the guardian-kid

GET guardian-kids/{GuardianKidId}
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

I also make those two end-point (you can only GET on those)

GET kids/{kidId}/guardian-kids
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

GET guardians/{guardianId}/guardian-kids
{
    "type": "parent",
    "guardian": {
        "id": "{guardianId}",
        "url": "guardians/{guardianId}/"
    },
    "kid": {
        "id": "{kidId}",
        "url": "kids/{kidId}/"
    },
    "url": "guardian-kids/{GuardianKidId}/"
}

The "problem" I saw with my other methods is that /kids/{kidID}/guardians/ and /guardians/ will not represent the same kind of ressources but have the same name.

slaynerM
  • 45
  • 2
  • 7