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 { schema } from 'dynamodb-toolbox/schema';
const PokemonEntity = new Entity({
name: 'POKEMON',
table: PokeTable,
schema: schema(...)
});
Constructor
Entity
takes a single parameter of type object
that 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).
☝️ The 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: schema({
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: schema({
// 👇 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: schema({
// 👇 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: schema({
// 👇 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: schema({
// 👇 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: schema({
// 👇 linked attributes should also be tagged as `.key()`
specifiers: list(string()).key(),
sk: number().key(),
...
}),
computeKey: ({ specifiers, sk }) => ({
pk: specifiers.join('#'),
sk
})
})
entityAttributeName
A string
to customize the name of the internal entity attribute (see Internal Attributes).
timestamps
A boolean
or object
to customize the internal created
and modified
attributes (see Internal Attributes).