Entity
Entities represent a category of items in your Table
.
An entity must belong to a Table
, but a Table
can contain items from several entities. DynamoDB-Toolbox is designed with Single Tables in mind, but works just as well with multiple tables and still makes your life much easier (e.g. for batch operations or transactions):
import { Entity } from 'dynamodb-toolbox/entity'
import { item } from 'dynamodb-toolbox/schema/item'
import { string } from 'dynamodb-toolbox/schema/string'
const PokemonEntity = new Entity({
name: 'POKEMON',
table: PokeTable,
schema: item({
name: string(),
...
})
})
Note that you can provide a map
schema to the Entity
constructor:
import { Entity } from 'dynamodb-toolbox/entity'
import { map } from 'dynamodb-toolbox/schema/map'
import { string } from 'dynamodb-toolbox/schema/string'
const PokemonEntity = new Entity({
name: 'POKEMON',
table: PokeTable,
schema: map({
name: string(),
...
})
})
See the Schema Section for more details.
Constructor
The Entity
constructor takes a single parameter of type object
and accepts the following properties:
name
(required)
A string
that uniquely identifies your entity:
const PokemonEntity = new Entity({
name: 'POKEMON',
...
})
DynamoDB-Toolbox automatically tags your items with their respective entity names (see Internal Attributes). While this can be opted out of, we strongly recommend keeping it enabled if you use Single Table Design.
☝️ A consequence is that name
cannot be updated once your Entity
has its first items* (at least not without a data migration first), so choose wisely!
schema
(required)
The schema
of the Entity
. See the Schema Section for more details on how to define schemas.
table
(required)
The Table
of the Entity
.
DynamoDB-Toolbox must check that an entity schema
matches its Table
primary key somehow. In simple cases, both schemas can simply fit:
- Direct match
- Renaming
- Prefixing
- Linked
const PokeTable = new Table({
partitionKey: { name: 'pk', type: 'string' },
sortKey: { name: 'sk', type: 'number' },
...
})
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
pk: string().key(),
sk: number().key(),
...
}),
})
const PokeTable = new Table({
partitionKey: { name: 'pk', type: 'string' },
sortKey: { name: 'sk', type: 'number' },
...
})
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
// 👇 renaming works
pokemonId: string().savedAs('pk').key(),
level: number().savedAs('sk').key(),
...
}),
})
import { prefix } from 'dynamodb-toolbox/transformers/prefix'
const PokeTable = new Table({
partitionKey: { name: 'pk', type: 'string' },
sortKey: { name: 'sk', type: 'number' },
...
})
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
// 👇 saved as `POKEMON#${pokemonId}`
pokemonId: string()
.transform(prefix('POKEMON'))
.savedAs('pk')
.key(),
level: number().savedAs('sk').key(),
...
})
})
👉 See the transformers section for more details on transformers.
const PokeTable = new Table({
partitionKey: { name: 'pk', type: 'string' },
...
})
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
// 👇 linked attributes should also be tagged as `.key()`
pokemonId: string().key(),
trainerId: string().key(),
...
}).and(prevSchema => ({
pk: string()
.key()
.link<typeof prevSchema>(
({ trainerId, pokemonId }) =>
`${trainerId}#${pokemonId}`
)
}))
})
computeKey
(potentially required, depending on schema
)
...but using schemas that don't fit is OK.
In this case, the Entity
constructor requires a computeKey
function to derive the primary key from the Entity
key attributes.
This can be useful for more complex cases like mapping several attributes to the same key:
- Renaming
- Composing
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
// 👇 linked attributes should also be tagged as `.key()`
pokemonId: string().key(),
level: number().key(),
...
}),
// 🙌 Types are correctly inferred!
computeKey: ({ pokemonId, level }) => ({
pk: pokemonId,
sk: level
})
})
const PokemonEntity = new Entity({
table: PokeTable,
schema: item({
// 👇 linked attributes should also be tagged as `.key()`
specifiers: list(string()).key(),
sk: number().key(),
...
}),
computeKey: ({ specifiers, sk }) => ({
pk: specifiers.join('#'),
sk
})
})
entityAttribute
A boolean
or object
to customize the internal entity
attributes (see Internal Attributes).
timestamps
A boolean
or object
to customize the internal created
and modified
attributes (see Internal Attributes).
Building Entity Actions
To allow for extensibility, better code-splitting and lighter bundles, Entities
only expose a .build(...)
method which acts as a gateway to perform Entity Actions:
import { GetItemCommand } from 'dynamodb-toolbox/entity/actions/get'
const { Item } = await PokemonEntity.build(GetItemCommand)
.key(key)
.send()
If you don't mind large bundle sizes, you can still use the EntityRepository
actions that expose all the others as methods.