UpdateAttributesCommand
Performs an UpdateAttributes Operation on an entity item. Similar to UpdateItemCommand except than deep attribute updates are non-partial:
import { UpdateAttributesCommand } from 'dynamodb-toolbox/entity/actions/updateAttributes'
const updateAttributesCommand = PokemonEntity.build(
UpdateAttributesCommand
)
const params = updateAttributesCommand.params()
await updateAttributesCommand.send()
Requestβ
.item(...)β
(required)
The attributes to update, including the key:
import { UpdateAttributesCommand } from 'dynamodb-toolbox/entity/actions/updateAttributes'
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: 12,
...
})
.send()
You can use the UpdateAttributesInput type to explicitly type an object as a UpdateAttributesCommand item object:
import type { UpdateAttributesInput } from 'dynamodb-toolbox/entity/actions/updateAttributes'
const item: UpdateAttributesInput<typeof PokemonEntity> = {
pokemonId: 'pikachu1',
level: 12,
...
}
await PokemonEntity.build(UpdateAttributesCommand).item(item).send()
UpdateAttributesInput differs from PutItemInput as the root attributes are partially required β except for always required attributes without defaults or links β and benefit from an extended syntax that reflects the capabilities of DynamoDB.
It also differs from UpdateItemInput as deep attributes (e.g. lists, maps and records) are always completely overridden by default.
Root attributesβ
Root attributes, wether flat or deep, benefit from the same syntax as the UpdateItemCommand command:
- $remove
- $get
- Flat attributes
- Numbers
- Sets
// π Extended syntax is taken from `UpdateItemCommand`
import { $remove } from 'dynamodb-toolbox/entity/actions/update'
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
// π clear 'statusEffect' from pokemon
statusEffect: $remove()
})
.send()
import { $get } from 'dynamodb-toolbox/entity/actions/update'
await PokemonEntity.build(UpdateAttributesCommand)
.item({
...
level: 42,
// π fill 'previousLevel' with current 'level'
previousLevel: $get('level')
})
.send()
await PokemonEntity.build(UpdateAttributesCommand)
.item({
...
// π Set fields to desired values
isLegendary: true,
nextLevel: 42,
name: 'Pikachu',
binEncoded: new Uint8Array(...),
skills: new Set(['thunder'])
})
.send()
import {
$add,
$subtract,
$get
} from 'dynamodb-toolbox/entity/actions/update'
await PokemonEntity.build(UpdateAttributesCommand)
.item({
...
// π lose 20 health points
health: $subtract($get('health'), 20),
// π gain 1 level
level: $sum($get('level', 0), 1),
// ...similar to
level: $add(1)
})
.send()
import {
$add,
$delete
} from 'dynamodb-toolbox/entity/actions/update'
await PokemonEntity.build(UpdateAttributesCommand)
.item({
...
skills: $add(new Set(['thunder', 'dragon-tail'])),
types: $delete(new Set(['flight']))
})
.send()
Deep attributesβ
In the case of deep attributes (e.g. lists, maps and records), updates are complete by default:
// π Complete overrides
await PokemonEntity.build(UpdateAttributesCommand).item({
...
// Resets list
skills: ['thunder'],
// Removes all other map attributes
some: {
deep: {
field: 'foo',
otherField: 42
}
},
// Removes all other record keys
bestSkillByType: {
electric: 'thunder'
}
})
Lists benefit from additional $append and $prepend extensions, which can use references:
import {
$append,
$prepend
} from 'dynamodb-toolbox/entity/actions/update'
PokemonEntity.build(UpdateAttributesCommand).item({
...
skills: $append(['thunder', 'dragon-tail']),
levelHistory: $append($get('level')),
types: $prepend(['flight']),
})
$append and $prepend are upserts: they create a new list if the attribute is missing from the item.
.options(...)β
Provides additional options:
await PokemonEntity.build(UpdateAttributesCommand)
.item(...)
.options({
returnValues: 'UPDATED_OLD',
capacity: 'TOTAL',
...
})
.send()
The options are the same as the UpdateItemCommand action options. Check the dedicated section for more details.
Examplesβ
- Basic
- Conditional
- Return values
- Multitenant
- Aborted
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: $add(1)
})
.send()
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: $add(1)
})
.options({
// π Makes sure that 'level' stays <= 99
condition: { attr: 'level', lt: 99 },
// π Includes the Item in the error if not so
returnValuesOnConditionFalse: 'ALL_OLD'
})
.send()
const { Attributes: prevPikachu } =
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: $add(1)
})
.options({ returnValues: 'ALL_OLD' })
.send()
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: $add(1)
})
.options({ tableName: `tenant-${tenantId}-pokemons` })
.send()
const abortController = new AbortController()
const abortSignal = abortController.signal
await PokemonEntity.build(UpdateAttributesCommand)
.item({
pokemonId: 'pikachu1',
level: $add(1)
})
.send({ abortSignal })
// π Aborts the command
abortController.abort()
Responseβ
The data is returned using the same response syntax as the DynamoDB UpdateItem API, with an additional ToolboxItem property, which allows you to retrieve the item generated by DynamoDB-Toolbox:
const { ToolboxItem: generatedPokemon } =
await PokemonEntity.build(UpdateAttributesCommand)
.item(...)
.send()
// π Great for auto-generated attributes
const modifiedTimestamp = generatedPokemon.modified
If present, the returned attributes are formatted by the Entity.
You can use the UpdateAttributesResponse type to explicitly type an object as an UpdateAttributesCommand response object:
import type { UpdateAttributesResponse } from 'dynamodb-toolbox/entity/actions/updateAttributes'
const response: UpdateAttributesResponse<
typeof PokemonEntity,
// π Optional options
{ returnValues: 'ALL_OLD' }
// π Typed as PokemonΒ | undefined
> = { Attributes: ... }