Edit

Share via


Use the If-Match HTTP Header in PUT and PATCH operations

For REST endpoints, developers often want control over whether updates create new records or only modify existing ones. The If-Match HTTP header provides that control in Data API builder (DAB).

By default, DAB treats PUT and PATCH as upsert operations:

  • If the resource exists: DAB updates it.

  • If it doesn't exist: DAB inserts it.

    • PUT → full upsert (replaces resource).
    • PATCH → incremental upsert (applies partial update).

Adding If-Match: * changes this behavior to update-only semantics.

What If-Match does in DAB

If-Match is supported only with the wildcard value *.

Header Value Behavior
If-Match: * Perform update only if the resource exists; if missing → 404 Not Found.
If-Match: <any other> Rejected; 400 Bad Request (Etags not supported, use '*').
(absent) Upsert behavior (insert if not found, otherwise update).

Behavior overview

  • DAB doesn't implement per-record ETag or version matching.
  • No concurrency token is evaluated. * only asserts "must exist."
  • Applies only to REST, not GraphQL.
  • Not currently meaningful for DELETE operations.

Using If-Match with PUT

Without If-Match, PUT inserts when the resource doesn't exist (returns 201 Created).

Update-only example

Important

Because PUT performs a full replacement, the request body must include all non-nullable columns. Omitting a required column causes a 400 Bad Request database error, even when If-Match: * is present. Use PATCH instead of PUT if you only want to send a subset of fields.

Request

PUT /api/Books/id/1
If-Match: *
Content-Type: application/json

{
  "title": "The Return of the King",
  "publisher_id": 7
}

Success (record existed)

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 1,
  "title": "The Return of the King",
  "publisher_id": 7
}

Failure (record missing)

HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "No Update could be performed, record not found"
}

Upsert insert example (no If-Match and record didn't exist)

Request

PUT /api/Books/id/500
Content-Type: application/json

{
  "title": "Inserted via PUT",
  "publisher_id": 7
}

Response

HTTP/1.1 201 Created
Location: id/500
Content-Type: application/json

{
  "id": 500,
  "title": "Inserted via PUT",
  "publisher_id": 7
}

Using If-Match with PATCH

PATCH behaves similarly. Without If-Match, it performs an incremental upsert. With If-Match: *, it only updates existing rows.

Request

PATCH /api/Books/id/1
If-Match: *
Content-Type: application/json

{
  "title": "The Two Towers"
}

Response when Success

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 1,
  "title": "The Two Towers"
}

Response when Not found

HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "error": "No Update could be performed, record not found"
}

Invalid If-Match Usage

Any value other than * (including quoted strings) is rejected.

Request

PUT /api/Books/id/1
If-Match: "abc123"
Content-Type: application/json

{
  "title": "To Kill a Mockingbird"
}

Response

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "error": "Etags not supported, use '*'"
}

Review

  • Omit If-Match for upsert (insert-or-update) semantics.
  • Use If-Match: * for strict update-only semantics (404 if the item is missing).
  • Don't use any other value. Real ETag matching isn't implemented.

Note

The Data API builder 2.0 functionality described in this section is currently in preview and might change before general availability. For more information, see What's new in version 2.0.