Should I update or upsert?

Upsert either updates an existing record, or inserts a new one. It's the more flexible strategy, but dealing with it is a bit more complex.

To update an object, the Unit21 API supports two different methods:

  • a) a PUT request to an /update endpoint, using the object's unit21_id
  • b) a POST request to a /create endpoint, using the object's external_id

The PUT option is the traditional RESTful method for handling updates.
The POST option lets you create and update objects through the same /create endpoint.
This option is called an upsert.

📘

Upsert = Update + insert

For any endpoint that supports upsert, you MAY update all objects through POST requests to /create.
In these cases, the /update endpoint is not strictly necessary.

For its flexibility and convenience, developers typically prefer to create and update objects through the single /create endpoint. However, using upserts makes queries more complex. For its simplicity, you might prefer to update with the PUT request in some cases.

Upsert creates and updates through POST requests to /create

The word "upsert" is a combination of the words "update" and "insert". When the Unit21 API receives a valid POST request to an endpoint that supports upserts, for each request's fields:

  • empty fields will have a new value inserted
  • non-empty fields will be updated

For example, you could create an event with a POST to /events/create, with the following request body:

{
  "general_data": {
    "event_id": "event1000",
    "event_type": "transaction",
    "event_time": 1623145011
  },
  "transaction_data": {
    "sender_instrument_id": "instrument-100",
    "amount": 15000
  }
}

This creates a new event, event1000:

{"event_id":"event1000","previously_existed":false,"unit21_id":"4969806"}

Now, if you want to update or add values, you could use the event_id to upsert with another POST to /events/create. The following request body does two things:

  • Updates the transaction_data.amount field
  • Inserts a new value to general_data.tags:
{
  "general_data": {
    "tags": [
      "suspicious:new_account"
    ],
    "event_id": "event1000",
    "event_type": "transaction",
    "event_time": 1623145011
  },
  "transaction_data": {
    "sender_instrument_id": "instrument-100",
    "amount": 25000
  }}

For single fields, like amount, the upserts simply overwrite the field with new posted values. For array fields, how the data merge depends on the options you pass. Data merging on updates covers these options detail.

Upsert benefits: one endpoint and batch updates

As you can create and update through one endpoint, upserts are convenient. Additionally, when you upsert, you do not need to wait for Unit21 to accept the entity in the system, as you do with a PUT.

Secondly, as the /create endpoints support batch uploads, you can also use upserts to update a large number of objects at the same time

Block upserts with the option upsert_on_conflict

To prevent POST requests from overwriting existing data, set the value for options.upsert_on_conflict to false. This strategy blocks upserts for individual objects and for batches.

Update provides simpler queries

The convenience of upserts comes with two tradeoffs:

  • To update a single field, an upsert MUST include all fields required by the endpoint
  • Upsert does not follow the standard syntax of REST APIs.

Some developers prefer to use the PUT method to update some or all Unit21 objects. Besides offering a method that's well-known to all developers familiar with REST, it also allows for simpler queries. For example, to update the event100 object created in the preceding section, a developer could also send a PUT to /events/{unit21_id}/update. The request body is noticeably shorter:

{
  "general_data": {
    "tags": [
      "suspicious:new_account"
    ],
  "transaction_data": {
    "amount": 25000
  }
}

As with upserts, you can pass options to configure how updates merge array data. Data merging on updates covers these options' details.