What is the best practice for API design to convert from one resource to another?

Hi,

Is there a best practice or common practice for conversions?

Given I have “orders” and “loans” and an order gets converted to multiple loans, one for each order line item, how would you design the “conversions” endpoint?

I'm thinking it belongs in loanManagement since it creates loans, it just goes through the order lines and pulls out line items, with equipment that will each be used to create a separate loan.

POST /loanManagement/v1/orderConversions { order details in body } 

Or alternatively on the orderMangement API, since it knows what a valid order is and how to parse an order correctly.

POST /orderManagement/v1/orders/{id}/loanConversions

Or as a separate API

POST /conversions/loans { order details in body } 

Thoughts?

0 5 322
5 REPLIES 5

Not applicable

I agree this belongs in loanManagement. I wonder how important the "conversion" concept is and whether it deserves a place in the URLs. I would focus on two ideas 1) I am creating loans 2) information for the new loans is derived from an order. The most obvious solution to me is to POST to /loans, with the body being a request to create loans from an order. Something like:

Request:

POST /loans HTTP/1.1 
Host: example.org
Content-Type: application/json
Content-Length: 61

{"type" : "CreateLoansForOrderRequest",
 "order": "/orders/12345"
}

One problem you may have with this design is the response. A POST request that creates a new entity should normally result in a 201 Created response code along with a Location header giving the URL of the newly-created resource. If the result of this POST is the creation of multiple, independent loans, then this pattern will not work and you may just have to return 200 OK.

Thanks @Martin, I should have mentioned that the POST /loans endpoint is used to create a loan in the typical sense, provide the required data based on the loan definition.

The CreateLoansFromOrder is a convenience API and should have its own endpoint such that data validation can be done on the order.

@Kurt Kanaskie That's fine, Kurt. As I said, the idea of an endpoint comes from RPC, and solving the problem that way is just fine—it is what most people do, so you are in good company. In a REST model, every URL identifies a thing. If you are designing in that model, you probably won't want URLs like /createLoansFromOrder, because it is difficult to answer the question "what is that thing?". If I did a GET on it instead of a POST, what would I get back? If I were designing this, I think I would stick with /loans and base the validation on the body. If the body has "type":"Loan", then I would validate and process it one way. If the body has "type":"LoansForOrder" I would validate and process it another. I might be attracted to /conversions/loans if a GET on that URL gave me a different result than a GET on /loans. That would mean that /conversions/loans really is a different entity from just /loans, it's not just a remote procedure masquerading as an entity. I'm not arguing that you are wrong—what you are doing will work and is probably what the majority of people would do—I'm just offering a different idea for you to think about.

@Martin Valid points and I generally agree. This is an API some might refer to as an experience or convenience or orchestration API. It is intended to make it easy for the App developer. Call POST /loans/conversions { order details } rather than looping through the "order" and making individual POST /loans { loan details }.

If I did a GET /loans/conversions I could (in theory) return all the "conversions" with references to the loan on each of the line items.

Not applicable

I agree with Martin, that from REST perspective we should always talk in "entities".

And I also think, that usually, we don't have to create any "virtual" entities like "conversions", since the existing entities are enough :).

If I understand correctly, the goal is to be able to easily create multiple loans based on the data of a single order. And I would suggest a slightly different approach.

To create loans based on an order:

POST /loans?fromOrder=1
{ order data }

To get all loans created from orders:

GET /loans?createdFromOrder=1

To get all loans created from a specific order:

GET /loans?order=123456