UpdateHandler#
com.palmyralabs.palmyra.handlers.UpdateHandler
Update lifecycle. Composes PreProcessor and a rollback failure hook. Each hook additionally receives the pre-image dbTuple so you can diff new vs. stored values.
Request / response — User#
Assuming the User model from QueryHandler → Worked example:
Request#
The URL carries the primary key (see @CrudMapping → {id}); the body carries the new values.
POST /api/v1/admin/user/102
Content-Type: application/json{
"firstName": "Ada",
"lastName": "Lovelace-Byron",
"status": "ACTIVE"
}Partial payloads are fine — Palmyra merges supplied fields onto the row identified by {id}. Omitted attributes are left untouched.
Response#
The post-update row:
{
"id": 102,
"loginName": "ada@example.com",
"firstName": "Ada",
"lastName": "Lovelace-Byron",
"status": "ACTIVE",
"tenantId": 3,
"createdAt": "2026-04-01T09:12:00Z",
"createdBy": "admin@example.com"
}Methods#
| Method | Signature |
|---|---|
validate |
void validate(Tuple tuple, HandlerContext ctx) |
preProcessRaw |
Tuple preProcessRaw(Tuple tuple, HandlerContext ctx) |
preProcess |
Tuple preProcess(Tuple tuple, HandlerContext ctx) |
aclCheck |
int aclCheck(Tuple tuple, HandlerContext ctx) |
getAcl |
int getAcl(Tuple tuple, HandlerContext ctx) |
preUpdate |
Tuple preUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) |
onUpdate |
Tuple onUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) |
postUpdate |
Tuple postUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) |
rollback |
default Tuple rollback(Tuple tuple, Tuple dbTuple, HandlerContext ctx) |
Example#
@Component
@CrudMapping(value = "/v1/admin/user", type = User.class)
public class UserUpdateHandler implements UpdateHandler {
@Autowired
private UserProvider userProvider;
@Autowired
private OutboxService outbox;
@Override
public int aclCheck(Tuple tuple, HandlerContext ctx) {
return userProvider.hasRole("USER_UPDATE") ? 0 : -1;
}
@Override
public Tuple preUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) {
tuple.set("updatedBy", userProvider.getUserId());
tuple.set("updatedAt", Instant.now());
return tuple;
}
@Override
public Tuple onUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) {
// detect email change and re-hash downstream identifiers
if (!Objects.equals(tuple.get("loginName"), dbTuple.get("loginName"))) {
tuple.set("loginHash", hash((String) tuple.get("loginName")));
}
return tuple;
}
@Override
public Tuple postUpdate(Tuple tuple, Tuple dbTuple, HandlerContext ctx) {
outbox.publish("user.updated", Map.of(
"id", tuple.get("id"),
"before", dbTuple,
"after", tuple
));
return tuple;
}
@Override
public Tuple rollback(Tuple tuple, Tuple dbTuple, HandlerContext ctx) {
outbox.discardPending(tuple.get("id"));
return tuple;
}
}