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).
metaβ
Attaches metadata to the Entity (as an object property).
The meta object can include a title and description, both of which must be strings. Additional fields can be of any type:
const PokemonEntity = new Entity({
...
meta: {
title: 'Pokemon',
description: 'An Awesome Entity for development use',
other: { field: 'of any type' }
}
})
// π Directly access/modify metadata
console.log(PokemonEntity.meta)
PokemonEntity.meta.foo = 'A new field'
Although optional, the meta property can be helpful in scenarios like describing a MCP Server or synchronizing with DynamoDB-Toolshack.
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.