Updating records
You can overwrite a full record with a request like this:
const user = await xata.db.Users.createOrReplace('myid', { name: 'Keanu Reeves' });
user = xata.records().upsert("Users", "myid", {
"name": "Keanu Reeves"
})
client, _ := xata.NewRecordsClient()
user, _ := client.Upsert(context.TODO(), xata.UpsertRecordRequest{
RecordRequest: xata.RecordRequest{
TableName: "Users",
},
RecordID: "myid",
Body: map[string]*xata.DataInputRecordValue{
"name": xata.ValueFromString("Keanu Reeves"),
},
})
// PUT https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}
{
"name": "Keanu Reeves"
}
Note that in the above example, the whole record was overwritten. If you want to update only some of the fields, see the Partial Update section below. Also, the xata.version
field was automatically incremented to 1. We'll talk more about this special column in the Optimistic Concurrency Control section.
In order to do a partial update to a record, you can use a request like this:
const user = await xata.db.Users.update('myid', { email: 'newemail@example.com' });
// or, using the `update` method on the record object:
user.update({ email: 'newemail@example.com' });
user = xata.records().update("Users", "myid", {
"email": "newemail@example.com"
})
client, _ := xata.NewRecordsClient()
user, _ := client.Update(context.TODO(), xata.UpdateRecordRequest{
RecordRequest: xata.RecordRequest{
TableName: "Users",
},
RecordID: "myid",
Body: map[string]*xata.DataInputRecordValue{
"email": xata.ValueFromString("newemail@example.com"),
},
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "UPDATE \"Users\" SET email = $1 WHERE id = $2 RETURNING *;",
"params": ["newemail@example.com", "myid"]
}
// PATCH https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}
{
"email": "newemail@example.com"
}
It's also possible to update a record if it exists, or create it if it doesn't exist:
const user = await xata.db.Users.createOrUpdate('myid', {
name: 'Keanu Reeves',
email: 'keanu@example.com'
});
user = xata.records().upsert("Users", "myid", {
"name": "Keanu Reeves",
"email": "keanu@example.com",
})
client, _ := xata.NewRecordsClient()
user, _ := client.Upsert(context.TODO(), xata.UpsertRecordRequest{
RecordRequest: xata.RecordRequest{
TableName: "Users",
},
RecordID: "myid",
Body: map[string]*xata.DataInputRecordValue{
"name": xata.ValueFromString("Keanu Reeves"),
"email": xata.ValueFromString("keanu@example.com"),
},
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "INSERT INTO \"Users\" (id, name, email) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET name = $2, email = $3 RETURNING *;",
"params": ["rec_c8hnbch26un1nl0rthkg", "Keanu Reeves", "keanu@example.com"]
}
// POST https://tutorial-ng7s8c.xata.sh/db/tutorial:main/tables/users/data/{record_id}
{
"email": "keanu@example.com",
"name": "Keanu Reeves"
}
The numeric types (int and float) support basic mathematical operations: addition, subtraction, multiplication, and division. These operations are performed in a transaction, which means they are atomic.
Here is a simple example that increments a counter:
const user = await xata.db.Users.update('rec_c8hnbch26un1nl0rthkg', { counter: { $increment: 1 } });
user = xata.records().update("Users", "rec_c8hnbch26un1nl0rthkg", {
"counter": {
"$increment": 1
}
})
recordsClient, _ := xata.NewRecordsClient()
user, _ := recordsClient.Transaction(context.TODO(), xata.TransactionRequest{
Operations: []xata.TransactionOperation{
xata.NewUpdateTransaction(xata.TransactionUpdateOp{
Table: "Users",
Id: "rec_c8hnbch26un1nl0rthkg",
Fields: map[string]any{"counter": map[string]int{"$increment": *xata.Int(1)}},
}),
},
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "UPDATE \"Users\" SET counter = counter + 1 WHERE id = $1 RETURNING *;",
"params": ["rec_c8hnbch26un1nl0rthkg"]
}
// PATCH https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}
{
"counter": {
"$increment": 1
}
}
And here is an example that adds 10 to the counter:
const user = await xata.db.Users.update('rec_c8hnbch26un1nl0rthkg', { counter: { $increment: 10 } });
user = xata.records().update("Users", "rec_c8hnbch26un1nl0rthkg", {
"counter": {
"$increment": 10
}
})
recordsClient, _ := xata.NewRecordsClient()
user, _ := recordsClient.Transaction(context.TODO(), xata.TransactionRequest{
Operations: []xata.TransactionOperation{
xata.NewUpdateTransaction(xata.TransactionUpdateOp{
Table: "Users",
Id: "rec_c8hnbch26un1nl0rthkg",
Fields: map[string]any{"counter": map[string]int{"$increment": *xata.Int(10)}},
}),
},
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "UPDATE \"Users\" SET counter = counter + 10 WHERE id = $1 RETURNING *;",
"params": ["rec_c8hnbch26un1nl0rthkg"]
}
// PATCH https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}
{
"counter": {
"$increment": 10
}
}
And here is an example that divides the counter by 2:
const user = await xata.db.Users.update('rec_c8hnbch26un1nl0rthkg', { counter: { $divide: 2 } });
user = xata.records().update("Users", "rec_c8hnbch26un1nl0rthkg", {
"counter": {
"$divide": 2
}
})
recordsClient, _ := xata.NewRecordsClient()
user, _ := recordsClient.Transaction(context.TODO(), xata.TransactionRequest{
Operations: []xata.TransactionOperation{
xata.NewUpdateTransaction(xata.TransactionUpdateOp{
Table: "Users",
Id: "rec_c8hnbch26un1nl0rthkg",
Fields: map[string]any{"counter": map[string]int{"$divide": *xata.Int(2)}},
}),
},
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "UPDATE \"Users\" SET counter = counter / 2 WHERE id = $1 RETURNING *;",
"params": ["rec_c8hnbch26un1nl0rthkg"]
}
// PATCH https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}
{
"counter": {
"$divide": 2
}
}
The following operations are supported:
$increment
- adds N to the current value$decrement
- subtracts N from the current value$multiply
- multiplies the current value by N$divide
- divides the current value by N
The SDK provides a method to update one or multiple columns in a Linked record via reference from the current table:
const record = await xata.db.table.getFirst();
const updatedLinkedRecord = await record?.linkName?.update({
linkedColumn: 'new content'
});
Each call only writes to a single table, so columns to update
should live in the same linked table.
After executing a request, you will notice that the version field of the record is incremented automatically. You can read the version in the special column xata.version
.
user.xata;
// {
// "version": 2,
// "createdAt": "2023-05-15T08:21:31.96526+01:00",
// "updatedAt": "2023-05-15T21:58:54.072595+01:00"
// }
{
"id": "rec_c8hnbch26un1nl0rthkg",
"email": "keanu@example.com",
"name": "Keanu Reeves",
"xata": {
"version": 2,
"createdAt": "2023-05-15T08:21:31.96526+01:00",
"updatedAt": "2023-05-15T21:58:54.072595+01:00"
}
}
{
"id": "rec_c8hnbch26un1nl0rthkg",
"email": "keanu@example.com",
"name": "Keanu Reeves",
"xata": {
"version": 2,
"createdAt": "2023-05-15T08:21:31.96526+01:00",
"updatedAt": "2023-05-15T21:58:54.072595+01:00"
}
}
The version
field can be used to perform optimistic concurrency control, also known as "optimistic locking". Let's say you have two users of your web applications that are both trying to update the same record. They both retrieve the record at version 0, modify a field, and then write it back. Without locking or version checks, it's possible that the users will overwrite each other's changes.
To prevent this, you can pass the ifVersion
parameter to the PUT request. The value for ifVersion
should be the value of xata.version
from when you retrieved the record. For example, if you want to update the record only if the version is 0, you can send the following request:
const updatedUser = await xata.db.Users.update(
'rec_c8hnbch26un1nl0rthkg',
{
name: 'Keanu Reeves',
email: 'keanu@example.com'
},
{
ifVersion: 0
}
);
user = xata.records().update(
"Users",
"rec_c8hnbch26un1nl0rthkg",
{
"name": 'Keanu Reeves',
"email": 'keanu@example.com'
},
if_version=0
)
client, _ := xata.NewRecordsClient()
user, _ := client.Update(context.TODO(), xata.UpdateRecordRequest{
RecordRequest: xata.RecordRequest{
TableName: "Users",
},
RecordID: "rec_c8hnbch26un1nl0rthkg",
Body: map[string]*xata.DataInputRecordValue{
"name": xata.ValueFromString("Keanu Reeves"),
"email": xata.ValueFromString("keanu@example.com"),
},
IfVersion: xata.Int(0),
})
// POST https://{workspace}.{region}.xata.sh/db/{db}:{branch}/sql
{
"statement": "UPDATE \"Users\" SET email = $1, name = $2 WHERE id = $3 AND \"xata.version\" = $4 RETURNING *;",
"params": ["keanu@example.com", "Keanu Reeves", "rec_c8hnbch26un1nl0rthkg", 0]
}
// PUT https://{workspace}.{region}.xata.sh/db/{db}:{branch}/tables/{table}/data/{record_id}?ifVersion=0
{
"email": "keanu@example.com",
"name": "Keanu Reeves"
}
If someone else has updated the record since you have read it, the API will get a 422 Unprocessable Entity response:
{
"message": "version condition not met",
"status": 422
}
In this case, the TypeScript SDK, returns null
.
The way to treat this error depends on your application. You might be able to re-read the record, resolve the conflict, and then re-try the update. Or you might need to show the user a conflict error message.
If you have multiple records to update, you can send them in a single call using the /transactions
endpoint. The Xata transactions API allows bulk updates where specified fields of records are modified. These updates require an ID. By default updates will fail when the IDs are not found in the database. However, enabling the upsert
flag to true
allows the creation of new records missing IDs. For more details, refer to the transactions updates documentation.
Great! We can now update data safely in our databases. Let's now explore how we delete data from a database. Alternatively, we can also look into inserting data or using transactions to perform multiple operations with a single call. We've got guides for each of these operations.