Exposed UPDATE REST API
Question

Hi!

I have a question regaring exposed REST APi's. Lets say we have a method to create a new invoice in our system. For this we have a POST method that needs to receive the following information in JSON.

INPUT of the BODY:

  1. Invoice
    1. InvoiceDate (text)
    2. ContactPerson (text)
  2. InvoiceLines (Array)
    1. ArticleCode (text)
    2. Price (currency)
    3. Amount (decimal)
    4. VATPercentage (integer)

OUTPUT:

  1. InvoiceId (Id)
  2. IsSuccess (Boolean)

All right, now lets say we also want to have a Exposed API Method to update an existing invoice. What should be the expected input for this method?

Of course we need to have the InvoiceId, so we know what Invoice needs to be updated. As well as the data of the Invoice, like the InvoiceDate and ContactPerson. But I'm wondering how the update of the InvoiceLines should be done, since here a list is required. Should it just give all the InvoiceLines as input for this method and server-side delete all current InvoiceLines and rebuild them again according to the input of the method. 

Or should the update method require some kind of InvoiceLineId so it knows what InvoiceLine should be updated. Than we should probably also have a kind of InvoiceLineDelete method to delete an InvoiceLine by InvoiceLineId. Also, than we need to return the InvoicelineId's during the creating of an Invoice, else it's impossible to know what Id's belong to the InvoiceLines of the Invoice.

I'm wondering how this is usually done, can anyone give me some advice on this?

Thanks for all the help!

Kind regards,

Bash Nie.


mvp_badge
MVP
Solution

Hi Bash Nie,

In general, if you have a 1:n relationship your REST service would look something like the below.

For parent records:

  • POST /Parents/ - create parent, may contain list of children in body, returns the Parent's Id;
  • PUT /Parents/{ParentId} - update parent in its entirety, replace all data;
  • PATCH /Parents/{ParentId} - partially update parent. Note that in OutSystems, it's unfortunately not possible to distinguish from a POST in this case (see also this idea), so you'll have to get a bit creative to detect differences;
  • DELETE /Parents/{ParentId} - delete/deactivate a parent;
  • GET /Parents/{ParentId} - return data of a parent, may include children.

For child records:

  • POST /Parents/{ParentId}/Childs - explicitly create a child record of a parent, returns Child's Id;
  • PUT /Parents/{ParentId}/Childs/{ChildId} - update child in its entirety, replace all data;
  • PATCH /Parents/{ParentId}/Childs/{ChildId}  - partially update child, see above for difficulties with OS;
  • DELETE /Parents/{ParentId}/Childs/{ChildId} - delete/deactivate a child;
  • GET /Parents/{ParentId}/Childs - get all children of a certain parent, may be superfluous if GET of Parent also returns children (though may be useful if Parent itself returns a lot of data);
  • GET /Parents/{ParentId}/Childs/{ChildId} - returns data of a single child

Note that a POST (create) of the Parent may instead of returning just the Parent's Id, return the entire created Parent object, including its children. This may be useful not only for the Child Ids, but also in case there's other data that the system fills or calculates. If you do not want that, you could also use a second call to get the list of children to obtain their Ids.

That said, if the children have another unique code, like a product Id (assuming a product can only be part of a single line), you could have the PUT/PATCH/DELETE/GET methods filter on that product Id (so with a search parameter) for update/retrieve/delete.



Hi,

I think in this case there can be 2 solutions according to me .

1. As you suggested get InvoiceLinesId in response and store in your database. By this way when you want to update can just these that particular InvoiceLines Details to update. But in this you need to write logic to save the returned InvoiceLinesId.

2. Every time when you want to update InvoiceLines, just send all invoice lines related to particular invoice again. On API end, it will delete all old invoice lines using one delete query and then will insert new data. So it will be clear operation with not much logic.

Personally I will use second approach until there is not any feature to  keep the update history of invoice lines.

Regards


Thanks for the quick reply!

I also prefer going for the second option, since this is probably the easiest way without much additional logic. 

However, just to be sure. Is this not a bad practice to delete all existing records and rebuild them again? Would like to know what is usually done (best-practice) in this kind of scenario's. 

Kind regards,

Bash Nie

mvp_badge
MVP

You shouldn't actually delete lines. Mark them inactive, but don't physically remove them. It'll be way too easy to commit fraud in such a system, everything should be logged!

That said, from a technical persepective deleting everything then rebuilding is also not desirable, as it is slower, index files are getting bigger, database logs will grow faster, etc.

mvp_badge
MVP

Hi Bash Nie,

Personally I'd rethink the flow of this. An invoice is typically created when something is shipped to a customer, detailing what is sent. If you need to update the invoice for whatever reason, you might want to keep the old invoice, marked as cancelled/revoked/whatever, and create a new invoice, with a new invoice number, linking back to the old invoice. Thus you create a traceable history, which may be needed for auditory purposes and prevent fraud.

If you're still in the process of creating the invoice, e.g. during order picking, you'd typically only add lines for the added items. If for some reason a line was added that should be removed, I would indeed have a "cancel line" or something (a DELETE on an /Invoice/{InvoiceId}/Line/{LineNumber} or {LineId}, or perhaps with an ItemId as search parameter and a count of the number of items to delete). But allowing posted orders to have their invoice changed is bad imho.

Hi Kilian and Vikas,

The scenario of the invoice and invoicelines is only an example of an one-to-many relation where the main entity also has one or more childs like the invoice with lines.

But oké. In most cases it would than still be better to not rebuild the whole list (by deleting and creating all records) during the update, right?

So than Ill need to do the following, if im not mistaken.

1. CREATE method that receives all invoice data and the list of invoiceline.

2. UPDATE method 

either A) method to update only invoice and another method to add lines and another method to update a specific line by Id?

Or B) method to update the whole invoice and lines. This method than also needs to receive the invoiceline id's that needs to be updated and the ones that are not sent should de delete?

3. DELETE method (only in case of 2A) to delete an invoiceline from an invoice by invoicelineId

Another question: how the output of the create method should look like? Given that we should return the id's of the created InvoiceLines as well? Since they are required for the update and delete method. I am not sure how to return those id's in the way that the consumer of the API knows which id belangs to the invoiceline that theyve send in, in the list.

Kind regards,

Bash Nie

mvp_badge
MVP
Solution

Hi Bash Nie,

In general, if you have a 1:n relationship your REST service would look something like the below.

For parent records:

  • POST /Parents/ - create parent, may contain list of children in body, returns the Parent's Id;
  • PUT /Parents/{ParentId} - update parent in its entirety, replace all data;
  • PATCH /Parents/{ParentId} - partially update parent. Note that in OutSystems, it's unfortunately not possible to distinguish from a POST in this case (see also this idea), so you'll have to get a bit creative to detect differences;
  • DELETE /Parents/{ParentId} - delete/deactivate a parent;
  • GET /Parents/{ParentId} - return data of a parent, may include children.

For child records:

  • POST /Parents/{ParentId}/Childs - explicitly create a child record of a parent, returns Child's Id;
  • PUT /Parents/{ParentId}/Childs/{ChildId} - update child in its entirety, replace all data;
  • PATCH /Parents/{ParentId}/Childs/{ChildId}  - partially update child, see above for difficulties with OS;
  • DELETE /Parents/{ParentId}/Childs/{ChildId} - delete/deactivate a child;
  • GET /Parents/{ParentId}/Childs - get all children of a certain parent, may be superfluous if GET of Parent also returns children (though may be useful if Parent itself returns a lot of data);
  • GET /Parents/{ParentId}/Childs/{ChildId} - returns data of a single child

Note that a POST (create) of the Parent may instead of returning just the Parent's Id, return the entire created Parent object, including its children. This may be useful not only for the Child Ids, but also in case there's other data that the system fills or calculates. If you do not want that, you could also use a second call to get the list of children to obtain their Ids.

That said, if the children have another unique code, like a product Id (assuming a product can only be part of a single line), you could have the PUT/PATCH/DELETE/GET methods filter on that product Id (so with a search parameter) for update/retrieve/delete.



Thanks, this is really helpfull!

One more question. In the PUT of the parent we should only update (receive) the parent data and not the childs, right? Unlike the parent post where we can create child records

For add/update/delete the child records, the child methods should be used, right?

Kind regards Bash Nie

mvp_badge
MVP

That would seem to make the most sense, but it depends on your exact use case. A PATCH would in my opinion definitely only update the parent, whether a PUT also receives and updates the children is up to you. But I would probably use the child methods for that, myself.

Agree with Kilian you should keep history of changes for invoicing. Although for now it will make your logic little complex but it will be able to handle auditing and fraud cases.

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.