Skip to main content
Examples

Create, update & delete

Insert, update and delete rows through a generated API - POST, PUT, PATCH and DELETE, request bodies, required roles, status codes and error responses.

Route Does Role needed Success
POST /api/<schema>/<table> Insert one row Read & Write or Admin 201 {"id": <new key>}
PUT /api/<schema>/<table>/<id> Update the columns you send Read & Write or Admin 204, empty body
PATCH /api/<schema>/<table>/<id> Update the columns you send, strictly validated Read & Write or Admin 204, empty body
DELETE /api/<schema>/<table>/<id> Delete one row Admin 204, empty body

Bodies are JSON objects whose keys are column names. The examples assume the setup from Authentication, with a Read & Write key (and an Admin key for DELETE).

Create

curl -s -X POST "$RESTWARD_API_URL/api/public/artist" \
  -H "X-API-Key: $RESTWARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "The Harness Band"}'
const res = await fetch(`${process.env.RESTWARD_API_URL}/api/public/artist`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ name: "The Harness Band" }),
});
const { id } = await res.json(); // res.status === 201
res = session.post(f"{api}/api/public/artist", json={"name": "The Harness Band"})
new_id = res.json()["id"]  # res.status_code == 201
var res = await http.PostAsJsonAsync("/api/public/artist", new { name = "The Harness Band" });
var created = await res.Content.ReadFromJsonAsync<Dictionary<string, JsonElement>>(); // 201
var id = created!["id"].GetInt32();
HTTP
HTTP/1.1 201
Content-Type: application/json

{"id":277}
  • The response carries the new primary-key value only. Read the row back with GET if you need the whole record.
  • Generated columns are the database's. Identity / auto-increment keys, sequence defaults and generated columns are filled by the database; a value you send for them is ignored, on create and on update. The OpenAPI document marks them readOnly.
  • Keys that are not columns of the table are ignored.

Update

PUT changes only the columns in the body; other columns keep their values. The primary key cannot be changed.

curl -s -X PUT "$RESTWARD_API_URL/api/public/artist/277" \
  -H "X-API-Key: $RESTWARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "The Harness Band (Remastered)"}'
const res = await fetch(`${process.env.RESTWARD_API_URL}/api/public/artist/277`, {
  method: "PUT",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ name: "The Harness Band (Remastered)" }),
}); // 204, or 404 when the row does not exist
res = session.put(f"{api}/api/public/artist/277", json={"name": "The Harness Band (Remastered)"})
# 204, or 404 when the row does not exist
var res = await http.PutAsJsonAsync("/api/public/artist/277", new { name = "The Harness Band (Remastered)" });
// 204 NoContent, or 404 NotFound when the row does not exist
HTTP
HTTP/1.1 204

PATCH or PUT?

Both change only the columns in the body. They differ in how strictly the body is checked:

Body contains PUT PATCH
An unknown column ignored 400 naming the column
The primary key ignored 400 (the key cannot be changed)
A generated column (identity, generated value) ignored 400
Nothing ({}) 400 400

Use PATCH when a typo should fail loudly:

curl -s -X PATCH "$RESTWARD_API_URL/api/public/album/348" \
  -H "X-API-Key: $RESTWARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Patch Sessions (Live)"}'
const res = await fetch(`${process.env.RESTWARD_API_URL}/api/public/album/348`, {
  method: "PATCH",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Patch Sessions (Live)" }),
}); // 204, 400 for a column that cannot be updated, 404 when the row does not exist
res = session.patch(f"{api}/api/public/album/348", json={"title": "Patch Sessions (Live)"})
var res = await http.PatchAsJsonAsync("/api/public/album/348", new { title = "Patch Sessions (Live)" });
HTTP
PATCH /api/public/album/348
{"album_id": 1}

HTTP/1.1 400
{"error":"Column 'album_id' is part of the primary key and cannot be changed.","column":"album_id"}

Delete

Deleting requires an Admin key.

curl -s -X DELETE "$RESTWARD_API_URL/api/public/artist/277" \
  -H "X-API-Key: $RESTWARD_ADMIN_KEY"
const res = await fetch(`${process.env.RESTWARD_API_URL}/api/public/artist/277`, {
  method: "DELETE",
  headers: { "X-API-Key": process.env.RESTWARD_ADMIN_KEY },
}); // 204, or 404 when the row does not exist
res = requests.delete(
    f"{api}/api/public/artist/277",
    headers={"X-API-Key": os.environ["RESTWARD_ADMIN_KEY"]},
)  # 204, or 404
using var admin = new HttpClient { BaseAddress = http.BaseAddress };
admin.DefaultRequestHeaders.Add("X-API-Key", Environment.GetEnvironmentVariable("RESTWARD_ADMIN_KEY"));
var res = await admin.DeleteAsync("/api/public/artist/277"); // 204, or 404
HTTP
HTTP/1.1 204

Deleting the same row again returns 404 with an empty body.

Errors

Error bodies have an error message and, when known, the constraint or column involved.

Situation Status Body
Body is not valid JSON, or not a JSON object 400 {"error":"The request body is not valid JSON."} / {"error":"The request body must be a JSON object."}
PUT with no column that can be updated 400 {"error":"The request body has no column of 'artist' that can be updated."}
PATCH with an empty body, or an unknown / key / generated column 400 {"error":"Unknown column 'genre' on 'album'.","column":"genre"}
Key role too low (Read Only writing, Read & Write deleting) 403 {"error":"API key role 'ReadWrite' is not authorized for this operation. Requires 'Admin' or higher."}
PUT/DELETE on a key that does not exist 404 empty
Duplicate key or unique value 409 {"error":"A row with the same unique value already exists (constraint 'playlist_track_pkey').","constraint":"playlist_track_pkey"}
DELETE of a row other rows still reference 409 {"error":"The row is still referenced by other rows (constraint 'album_artist_id_fkey').","constraint":"album_artist_id_fkey"}
Body larger than 1 MB 413 {"error":"Request body exceeds maximum allowed size of 1MB."}
Value of the wrong type ("artist_id": "one") 422 {"error":"Value 'one' is not valid for column 'artist_id' of type 'integer'.","column":"artist_id"}
Required column missing or null 422 {"error":"Column 'title' is required.","column":"title"}
Reference to a row that does not exist 422 {"error":"A referenced row does not exist (constraint 'album_artist_id_fkey').","constraint":"album_artist_id_fkey"}
Check constraint violated 422 {"error":"A value violates a check constraint (constraint '…').","constraint":"…"}
Value too long, out of range or wrongly formatted for the database 422 {"error":"A value is not valid for its column (too long, out of range or wrongly formatted)."}
Any other database error 500 {"error":"A database error occurred while creating the record."} (or updating / deleting)

Constraint and column names come from your schema; the database's own error message is never returned. Required columns and referenced keys are in the table's schema in the OpenAPI document.

Reconnecting to server...

Please wait while we restore your connection

Connection lost

Retrying in seconds...

Unable to reconnect

The server connection has been lost.
You may need to log in again.

Session Paused

The server has temporarily paused your session.

Session Expired

Your session could not be resumed.
Please reload the page to continue.