简体   繁体   中英

REST API - Single vs Bulk POST / PATCH (SQL INSERT / UPDATE)

I know that this question has been asked a bunch of times, but I they don't address the database SQL part, and don't have a suitable example.

The goal is to insert or update an invoice in a database, more specifically insert or update the items in the invoice, using a REST API.


Data

The data is comprised of two parts.

let invoiceHeader = {
  id: 123,
  key: "2019/00001",
  client: 42
}

let invoiceItems = [
  { id: 2001, sku: "A12345", quantity: 10, price: 300 },
  { id: 2002, sku: "B54321", quantity: 6, price: 500 }, 
  { id: 2003, sku: "C11223", quantity: 20, price: 200 },
]

Database

This data needs to be inserted into 2 database tables.

invoice
+-----+------------+--------+
| id  |    key     | client |
+-----+------------+--------+
| 123 | 2019/00001 |   42   |
+-----+------------+--------+

invoiceItems
+------+--------------+--------+----------+-------+
|  id  | fk_invoiceId |  sku   | quantity | price |
+------+--------------+--------+----------+-------+
| 2001 |          123 | A12345 |       10 |   300 |
| 2002 |          123 | B54321 |        6 |   500 |
| 2003 |          123 | C11223 |       20 |   200 |
+------+--------------+--------+----------+-------+

Invoice Header

The invoice header is straightforward

Insert Invoice Header (Create new invoice)

POST http://example.com/api/invoices
{
  "key": "2019/00001",
  "client": 42
}

INSERT INTO invoice (key, client) VALUES ('2019/00001', 42);

Update Invoice Header (with new client)

PATCH http://example.com/api/invoices/123
{
  "id": 123,
  "client": 66
}

UPDATE invoice SET client = 66 WHERE id = 123;

Invoice Items

However, the invoice items can be done in 2 ways.

Approach 1 - Single

Insert Invoice Items

POST http://example.com/api/invoices/123/items
{ "sku": "A12345", "quantity": 10, "price": 300 }
INSERT INTO invoiceItems (sku, quantity, price) VALUES ('A12345', 10, 300);

POST http://example.com/api/invoices/123/items
{ "sku": "B54321", "quantity": 6, "price": 500 }
INSERT INTO invoiceItems (sku, quantity, price) VALUES ('B54321', 6, 500);

POST http://example.com/api/invoices/123/items
{ "sku": "C11223", "quantity": 20, "price": 200 }
INSERT INTO invoiceItems (sku, quantity, price) VALUES ('C11223', 20, 200);

Update Invoice item (new quantity and price)

PATCH http://example.com/api/invoices/123/items/2001
{ "id": 2001, "quantity": 23, "price": 130 }
UPDATE invoiceItems SET quantity = 23, price = 130 WHERE id = 2001;

Approach 2 - Bulk

Insert Invoice Items

POST http://example.com/api/invoices/123/items
[
  { "sku": "A12345", "quantity": 10, "price": 300 },
  { "sku": "B54321", "quantity": 6, "price": 500 }, 
  { "sku": "C11223", "quantity": 20, "price": 200 },
]

INSERT INTO invoiceItems (sku, quantity, price) 
VALUES 
  ('A12345', 10, 300),
  ('B54321', 6, 500),
  ('C11223', 20, 200)
;

Update Invoice item (new quantity and price for a single item)

PATCH http://example.com/api/invoices/123/items
[
  { "id": 2001, "sku": "A12345", "quantity": 23, "price": 130 },
  { "id": 2002, "sku": "B54321", "quantity": 6, "price": 500 }, 
  { "id": 2003, "sku": "C11223", "quantity": 20, "price": 200 },
]

INSERT INTO table (id, sku, quantity, price) 
VALUES 
    (2001, 'A12345', 10, 300),
    (2002, 'B54321', 6, 500),
    (2003, 'C11223', 20, 200)
ON DUPLICATE KEY UPDATE 
    sku = values(sku),
    quantity = values(quantity),
    price = values(price)
;

Summary

The single approach makes a ton of requests, while the bulk approach sends unnecessary data.

Keep in mind that an invoice can easily have 300+ items.

So, which approach is better in your opinion?

Why not:

POST /566f5944-ba10-4ff5-8061-1b6b04a436c6

{
   invoiceHeader : {
     id: 123,
     key: "2019/00001",
     client: 42
   }, 
   invoiceItems : [
     { id: 2001, sku: "A12345", quantity: 10, price: 300 },
     { id: 2002, sku: "B54321", quantity: 6, price: 500 }, 
     { id: 2003, sku: "C11223", quantity: 20, price: 200 },
   ]
}

There is no reason that the spelling of the target-uri in a request needs to describe the semantics of the resource. You can do that, and it will make things easier for humans that understand your spelling conventions, but the machines don't care.

The payloads in the HTTP messages are documents - HTTP is an application protocol for transferring documents over a network ( Jim Webber, 2011 ). It doesn't particularly care what the documents are, so it makes sense to design documents that fit the needs of your domain.

The fact that this document is stored on the server as multiple rows of multiple tables is not relevant to the the API design. That's part of the point of REST, everything just looks like a web site.

What we might care about is the granularity of the resource and its impact on latency and caching. Big resources require fewer round trips, graphs of smaller resources give you finer grain cache control.

Again, think web page -- this question on stack overflow is a "resource", which includes hypermedia links to other resources with different caching properties (the logo and icon images, the java script, and so on). The browser is able to save some bandwidth because it knows (thanks to standardized meta data) that the currently cached copy of the logo is still valid.

Why is the API route a hash?

To emphasize that the spelling of the target-uri doesn't matter:

POST /566f5944-ba10-4ff5-8061-1b6b04a436c6
POST /api/invoices
POST /invoices
POST /purpleMonkeyDishwasher

These are all fine .

It seems a lot of overhead for inserting one item, to have everything sent along with it.

Right. So if the server has a resource that looks like that entire invoice, and you want to make only a small change, you might use HTTP Patch with a patch document format that describes your change. It's perfectly reasonable, once you have a handle on your representations, to decide to support more alternatives for "edit".

The key point to recognize is that in HTTP we are manipulating "documents". PUT , and also PATCH , are messages that ask the server to make the server's copy of the document look like your local copy. POST is similar ("please, Mr. Server, change your copy of this document"), but much less specific about what the change actually is.

Changes to the domain are side effects of the changes to the documents, and the changes to the domain might in turn produce changes in other documents.

But the key idea is to build a domain application protocol on top of the abstraction of a document store with a uniform message interface.

How can I handle the mixture of both insertions and updates ie user inserts new data and also changes existing data? Which HTTP method should I use? How would this look like?

If you are using remote authoring semantics (make your copy look like my copy), then you make whatever edits you need to to your local representation of the document, and then either (a) PUT a complete copy of your representation of the document, or (b) calculate a patch document representation of the changes you have made, and PATCH with a representation of the patch document.

It may help to review RFC 6901 JSON-Patch , which describes a standard semantics for different edits that you might make to a JSON document.

If you aren't using remote authoring semantics, then use POST. For example, most of the world wide web uses HTML Forms and application/x-www-form-urlencoded representations to communicate changes to the server.

POST is HTTP method with the most forgiving semantics; it doesn't promise to be safe, or idempotent, the result is only cacheable under very precise conditions, intermediate components aren't really allowed to make any assumptions other than the fact that a successful POST invalidates previously cached representations of the target resource.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM