Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

Best practices approach for linking and un-linking 2 separate resources

Not applicable

I am building a REST API and need to relate 2 standalone entities together. Having done a full day of Googling I'm no further to having confidence in the correct approach for handling this; therefore I'm seeking advice on the best practice for handling this scenario.

The resources represent an item for sale and the rates of that item. The item can have many associated rates to cater for backdated payments. The application needs to manage the 2 resources separately, but be able to relate the rates to the item once everything has been set up. These entities also need to support unlinking too.

The resources are as follows:

ShopItem
{
    shopItemId: 1,
    title: "sample title",
    rates: https://sample/restapi/shopItem/{id}/rates
}

Rate
{
    rateId: 1,
    title: "title of the rate",
    cost: 100,
    validFrom: "dateTime",
    validTo: "dateTime"
}

The options I have so far would be

1. Use PUT and the body would be a collection of Rate IDs.

This handles both link and unlink.

https://sample/restapi/shopItem/{id}/rates

{  
   [
      {   
         rateId: 1
      },
      {     
         rateId: 2
      }
   ]
}

2. Use POST and the body again would be a collection of Rate IDs.

This handles only link

https://sample/restapi/shopItem/{id}/rates

{  
   [
      {   
         rateId: 1
      },
      {     
         rateId: 2
      }
   ]
}

To handle unlink, a DELETE would be used:

https://sample/restapi/shopItem/{id}/rates

{  
   [
      {   
         rateId: 1
      },
      {     
         rateId: 2
      }
   ]
}

3. Use POST but handle only one resource at a time

This handles only link

https://sample/restapi/shopItem/{id}/rates

{     
   rateId: 1
}

To handle unlink, a DELETE would be used on the shop-item-rate resource. Only can handle one at a time:

https://sample/restapi/shopItemRates

{     
   rateId: 1
}

Hopefully this explains the scenario. Any advice is appreciated.

Solved Solved
0 3 498
1 ACCEPTED SOLUTION

A couple observations:

  • Consider employing IDs and URLs in the API design for linked items.

    GET /item/{itemid}/rates
    	

    returns:

    200 OK
    {
      "rates": [ 
         { 
           "id" : "1",  
           "href" : "/rates/48782" 
         },
         { 
           "id" : "2",  
           "href" : "/rates/3763837" 
         }
      ]
    }	

    or to get one rate:

    GET /item/{itemid}/rates/1
    	

    returns:

    200 OK
    { 
       "id" : "1",  
       "href" : "/rates/48782" 
    }	
  • You can decide if you want your API to resolve the rates when querying the item.

    GET /item/{itemid}/rates
    	

    returns:

    200 OK
    {
      "rates": [ 
         { 
           "id" : "1",  
           "href" : "/rates/48782",
           "rate" : 17.95,
           "etc" : "other rate-specific information here" 
         },
         { 
           "id" : "2",  
           "href" : "/rates/3763837", 
           "rate" : 17.95,
           "etc" : "other rate-specific information here" 
         }
      ]
    }
    	
  • HTTP DELETE does not accept a payload.
    You can use DELETE for your unlink action, but it will be a no-payload request.

    DELETE /item/{itemid}/rates/1
    	

    returns:

    200 OK
    {
       "id" : "2",  
       "href" : "/rates/3763837"
    }
    	

    And the error case:

    DELETE /item/{itemid}/rates/17
    	

    returns:

    404 Not Found
    {
      error: "rate not found", "id": "17" }
    }
    	
  • Whether you accept POST or PUT is a matter of preference and style.
    Some APIs allow POST to mean "create" while PUT is "update". This means they would not be mutually exclusive. Your API could support both. But you can use POST with a query param to denote "adding" a rate.

    POST /item/{itemid}?action=addRate
    {
      "href" : "/rates/96221"
    }
    	

    returns:

    200 OK
    {
      "rates": [ 
         { 
           "id" : "2",
           "href" : "/rates/3763837"
         },
         { 
           "id" : "3",
           "href" : "/rates/96221"
         }
      ]
    }	

    and the error case in which the rate-to-be-added does not exist:

    POST /item/{itemid}?action=addRate
    {
      "href" : "/rates/05461" 
    }
    	

    returns:

    404 Not Found
    {
      "error" : "rate does not exist", "rate" : "/rates/05461" 
    }
    	

    You could also modify the URL to be:

    POST /item/{itemid}/rates?action=add
    

View solution in original post

3 REPLIES 3