Schema
new dynamoose.Schema(schema[, options])
You can use this method to create a schema. The schema
parameter is an object defining your schema, each value should be a type or object defining the type with additional settings (listed below).
The options
parameter is an optional object with the following options:
Name | Type | Default | Information |
---|---|---|---|
saveUnknown | array | boolean | false | This setting lets you specify if the schema should allow properties not defined in the schema. If you pass true in for this option all unknown properties will be allowed. If you pass in an array of strings, only properties that are included in that array will be allowed. If you pass in an array of strings, you can use * to indicate a wildcard nested property one level deep, or ** to indicate a wildcard nested property infinite levels deep (ex. ["person.*", "friend.**"] will allow you store a property person with 1 level of unknown properties and friend with infinitely nested level unknown properties). If you retrieve items from DynamoDB with saveUnknown enabled, all custom Dynamoose types will be returned as the underlying DynamoDB type (ex. Dates will be returned as a Number representing number of milliseconds since Jan 1 1970). |
timestamps | boolean | object | false | This setting lets you indicate to Dynamoose that you would like it to handle storing timestamps in your items for both creation and most recent update times. If you pass in an object for this setting you must specify two keys createdAt & updatedAt , each with a value of a string or array of strings being the name of the attribute(s) for each timestamp. You can also set each of the createdAt & updatedAt properties equal to a Schema object. The keys of this Schema object represent the name of the attributes, with the value allowing for customization such as changing the storage type of the date. If you pass in null for either of those keys that specific timestamp won't be added to the schema. If you set this option to true it will use the default attribute names of createdAt & updatedAt . |
get | function | async function | undefined | You can use a get function on the schema to be run whenever retrieving an item from DynamoDB. Dynamoose will pass the entire item into this function and you must return the new value of the entire object you want Dynamoose to return to the application. This function will be run after all property get functions are run. |
set | function | async function | undefined | You can use a set function on the schema to be run whenever saving an item to DynamoDB. It will also be used when retrieving an item (ie. get , query , update , etc). Dynamoose will pass the entire item into this function and you must return the new value of the entire object you want Dynamoose to save to DynamoDB. This function will be run after all property set functions are run. |
validate | function | async function | undefined | You can use a validate function on the schema to ensure the value passes a given validation before saving the item. Dynamoose will pass the entire item into this function and you must return a boolean (true if validation passes or false if validation fails) or throw an error. This function will be run after all property validate functions are run. |
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"age": Number
}, {
"saveUnknown": true,
"timestamps": true
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"person": Object,
"friend": Object
}, {
"saveUnknown": [
"person.*", // store 1 level deep of nested properties in `person` property
"friend.**" // store infinite levels deep of nested properties in `friend` property
],
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"age": {
"type": Number,
"default": 5
}
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"name": String
}, {
"timestamps": {
"createdAt": "createDate",
"updatedAt": null // updatedAt will not be stored as part of the timestamp
}
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"name": String
}, {
"timestamps": {
"createdAt": ["createDate", "creation"],
"updatedAt": ["updateDate", "updated"]
}
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"name": String
}, {
"timestamps": {
"createdAt": {
"created_at": {
"type": {
"value": Date,
"settings": {
"storage": "iso"
}
}
}
},
"updatedAt": {
"updated": {
"type": {
"value": Date,
"settings": {
"storage": "seconds"
}
}
}
}
}
});
const dynamoose = require("dynamoose");
const schema = new dynamoose.Schema({
"id": String,
"name": String
}, {
"validate": (obj) => {
if (!obj.id.beginsWith(name[0])) {
throw new Error("id first letter of name.");
}
return true;
}
});
schema.hashKey
This property returns the property name of your schema's hash key.
const schema = new dynamoose.Schema({"id": String});
console.log(schema.hashKey); // "id"
schema.rangeKey
This property returns the property name of your schema's range key. It will return undefined if a range key does not exist for your schema.
const schema = new dynamoose.Schema({"id": String, "type": {"type": String, "rangeKey": true}});
console.log(schema.rangeKey); // "type"
const schema = new dynamoose.Schema({"id": String});
console.log(schema.rangeKey); // undefined
schema.indexAttributes
This property returns an array of strings with each string being the name of an attribute. Only attributes that are indexes are returned.
const schema = new Schema({
"id": String,
"name": {
"type": String,
"index": true
}
});
console.log(schema.indexAttributes); // ["name"]
Attribute Types
Type | Set Allowed | DynamoDB Type | Custom Dynamoose Type | Nested Type | Settings | Notes |
---|---|---|---|---|---|---|
String | True | S | False | False | ||
Boolean | False | BOOL | False | False | ||
Number | True | N | False | False | ||
Buffer | True | B | False | False | ||
Date | True | N | S (if storage is set to iso ) | True | False | storage - milliseconds | seconds | iso (default: milliseconds) | Will be stored in DynamoDB as milliseconds since Jan 1 1970, and converted to/from a Date instance. |
Object | False | M | False | True | ||
Array | False | L | False | True | ||
dynamoose.type.NULL | False | NULL | False | False | ||
Schema | False | M | True | True | This will be converted to an Object type. | |
Model | Only if no rangeKey for model's schema | S | N | B | M | True | If rangeKey in model's schema | Model Types are setup a bit differently. Read below for more information. | |
Combine | False | S | True | False | attributes - [string] - The attributes to store in the combine attribute. separator - string (default: , ) - The string used to separate the attributes in the combine attribute. | When running Model.update you must update all the attributes in the combine attributes array, or none of them. This is to ensure your combine method remains in sync with your overall item. |
Constant | False | S | N | BOOL | True | False | value - string | number | boolean - The value this attribute should always match. |
Set's are different from Array's since they require each item in the Set be unique. If you use a Set, it will use the underlying JavaScript Set instance as opposed to an Array. If you use a set you will define the type surrounded by brackets in the schema
setting. For example to define a string set you would do something like:
{
"friends": {
"type": Set,
"schema": [String]
}
}
When using saveUnknown
with a set, the type recognized by Dynamoose will be the underlying JavaScript Set constructor. If you have a set type defined in your schema the underlying type will be an Array.
Custom Dynamoose Types are not supported with the saveUnknown
property. For example, if you wish you retrieve an item with a Date type, Dynamoose will return it as a number if that property does not exist in the schema and saveUnknown
is enabled for that given property.
For types that are Nested Types
, you must define a schema
setting that includes the nested schema for that given attribute.
You can also define an array of types to allow your attribute to match any one of multiple types you set. For example in the following code example, the data
attribute can either be of type String or Number.
{
"data": [String, Number]
}
In the event you have multiple types that match (Date & Number, Set & Array, multiple Objects with different Schemas), Dynamoose will attempt to pick the closest matching type. However, if all types are valid, Dynamoose will default to the first type in the array.
{
"date": [Number, Date] // If you pass in a Date instance, it will use Date, otherwise it will use Number. All retrieved items from DynamoDB will use Number since there is no difference in the underlying storage of Number vs Date
}
You are also not allowed to have multiple types on any hashKey
or rangeKey
attributes. DynamoDB requires that these key attributes only have one type.
Model Types
For Model types, you must pass in another model or dynamoose.type.THIS
(to reference your own model).
const userSchema = new dynamoose.Schema({
"id": String,
"name": String,
"parent": dynamoose.type.THIS
});
const gameSchema = new dynamoose.Schema({
"id": String,
"state": String,
"user": User
});
const gameSchema = new dynamoose.Schema({
"id": String,
"state": String,
"user": {
"type": Set,
"schema": [User] // Set is only valid if `User` does not have a `rangeKey`
}
});
You can then set items to be an Item instance of that Model, a value of the hashKey
(if you don't have a rangeKey
), or an object representing the hashKey
& rangeKey
(if you have a rangeKey
).
const dad = new User({
"id": 1,
"name": "Steve"
});
const user = new User({
"id": 2,
"name": "Bob",
"parent": dad
});
const user = new User({
"id": 2,
"name": "Bob",
"parent": 1 // Only valid if you do not have a `rangeKey` on the model you are referencing
});
const user = new User({
"id": 2,
"name": "Bob",
"parent": {
"pk": 1,
"sk": "random"
} // Only valid if you have a `rangeKey` on the model you are referencing
});
You can then call item.populate
to populate the instances with the items.
const user = await User.get(2); // {"id": 2, "name": "Bob", "parent": 1}
const populatedUser = await user.populate(); // {"id": 2, "name": "Bob", "parent": {"id": 1, "name": "Steve"}}
Attribute Settings
type: type | object
The type attribute can either be a type (ex. Object
, Number
, etc.) or an object that has additional information for the type. In the event you set it as an object you must pass in a value
for the type, and can optionally pass in a settings
object.
{
"address": {
"type": Object
}
}
{
"deletedAt": {
"type": {
"value": Date,
"settings": {
"storage": "seconds" // Default: milliseconds (as shown above)
}
}
}
}
{
"data": {
"type": {
"value": "Constant",
"settings": {
"value": "Hello World" // Any `data` attribute must equal `Hello World` now.
}
}
}
}
schema: object | array
This property is only used for the Object
or Array
attribute types. It is used to define the schema for the underlying nested type. For Array
attribute types, this value must be an Array
with one element defining the schema. This element for Array
attribute types can either be another raw Dynamoose type (ex. String
), or an object defining a more detailed schema for the Array
elements. For Object
attribute types this value must be an object defining the schema. Some examples of this property in action can be found below.
{
"address": {
"type": Object,
"schema": {
"zip": Number,
"country": {
"type": String,
"required": true
}
}
}
}
{
"friends": {
"type": Array,
"schema": [String]
}
}
{
"friends": {
"type": Array,
"schema": [{
"type": Object,
"schema": {
"zip": Number,
"country": {
"type": String,
"required": true
}
}
}]
}
}
You can also define an array attribute that accepts more than one data type. The following example will allow the friends
attribute to be an array of strings, or an array of numbers, but the elements in the array must all be strings or must all be numbers.
{
"friends": {
"type": Array,
"schema": [
{
"type": Array,
"schema": [String]
},
{
"type": Array,
"schema": [Number]
}
]
}
}
default: value | function | async function
You can set a default value for an attribute that will be applied upon save if the given attribute value is null
or undefined
. The value for the default property can either be a value or a function that will be executed when needed that should return the default value. By default there is no default value for attributes.
Default values will only be applied if the parent object exists. This means for values where you apply a default
value to a nested attribute, it will only be applied if the parent object exists. If you do not want this behavior, consider setting a default
value for the parent object to an empty object ({}
) or an empty array ([]
).
{
"age": {
"type": Number,
"default": 5
}
}
{
"age": {
"type": Number,
"default": () => 5
}
}
You can also pass in async functions or a function that returns a promise to the default property and Dynamoose will take care of waiting for the promise to resolve before saving the object.
{
"age": {
"type": Number,
"default": async () => {
const networkResponse = await axios("https://myurl.com/config.json").data;
return networkResponse.defaults.age;
}
}
}
{
"age": {
"type": Number,
"default": () => {
return new Promise((resolve) => {
setTimeout(() => resolve(5), 1000);
});
}
}
}
forceDefault: boolean
You can set this property to always use the default
value, even if a value is already set. This can be used for data that will be used as sort or secondary indexes. The default for this property is false.
{
"age": {
"type": Number,
"default": 5,
"forceDefault": true
}
}
validate: value | RegExp | function | async function
You can set a validation on an attribute to ensure the value passes a given validation before saving the item. In the event you set this to be a function or async function, Dynamoose will pass in the value for you to validate as the parameter to your function. Validation will only be run if the item exists in the item. If you'd like to force validation to be run every time (even if the attribute doesn't exist in the item) you can enable required
.
{
"age": {
"type": Number,
"validate": 5 // Any object that is saved must have the `age` property === to 5
}
}
{
"id": {
"type": String,
"validate": /ID_.+/gu // Any object that is saved must have the `id` property start with `ID_` and have at least 1 character after it
}
}
{
"age": {
"type": String,
"validate": (val) => val > 0 && val < 100 // Any object that is saved must have the `age` property be greater than 0 and less than 100
}
}
{
"email": {
"type": String,
"validate": async (val) => {
const networkRequest = await axios(`https://emailvalidator.com/${val}`);
return networkRequest.data.isValid;
} // Any object that is saved will call this function and run the network request with `val` equal to the value set for the `email` property, and only allow the item to be saved if the `isValid` property in the response is true
}
}
required: boolean
You can set an attribute to be required when saving items to DynamoDB. By default this setting is false
.
In the event the parent object is undefined and required
is set to false
on that parent attribute, the required check will not be run on child attributes.
{
"email": {
"type": String,
"required": true
}
}
{
"data": {
"type": Object,
"schema": {
"name": {
"type": String,
"required": true // Required will only be checked if `data` exists and is not undefined
}
}
"required": false
}
}
enum: array
You can set an attribute to have an enum array, which means it must match one of the values specified in the enum array. By default this setting is undefined and not set to anything.
This property is not a replacement for required
. If the value is undefined or null, the enum will not be checked. If you want to require the property and also have an enum
you must use both enum
& required
.
{
"name": {
"type": String,
"enum": ["Tom", "Tim"] // `name` must always equal "Tom" or "Tim"
}
}
get: function | async function
You can use a get function on an attribute to be run whenever retrieving an item from DynamoDB. This function will only be run if the item exists in the item. Dynamoose will pass the DynamoDB value into this function and you must return the new value that you want Dynamoose to return to the application.
{
"id": {
"type": String,
"get": (value) => `applicationid-${value}` // This will prepend `applicationid-` to all values for this attribute when returning from the database
}
}
set: function | async function
You can use a set function on an attribute to be run whenever saving an item to DynamoDB. It will also be used when retrieving an item based on this attribute (ie. get
, query
, update
, etc). This function will only be run if the attribute exists in the item. Dynamoose will pass the value you provide into this function and you must return the new value that you want Dynamoose to save to DynamoDB.
{
"name": {
"type": String,
"set": (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}` // Capitalize first letter of name when saving to database
}
}
Unlike get
, this method will additionally pass in the original value as the second parameter (if available). Internally Dynamoose uses the item.original()
method to access the original value. This means that using Model.batchPut
, Model.update
or any other item save method that does not have access to item.original()
this second parameter will be undefined
.
{
"name": {
"type": String,
"set": (newValue, oldValue) => `${newValue.charAt(0).toUpperCase()}${newValue.slice(1)}-${oldValue.charAt(0).toUpperCase()}${oldValue.slice(1)}` // Prepend the newValue to the oldValue (split by a `-`) and capitalize first letter of each when saving to database
}
}
index: boolean | object | array
Indexes on your DynamoDB tables must be defined in your Dynamoose schema. If you have the update option set to true on your model settings, and a Dynamoose schema index does not already exist on the DynamoDB table, it will be created on model initialization. Similarly, indexes on your DynamoDB table that do not exist in your Dynamoose schema will be deleted.
If you pass in an array for the value of this setting it must be an array of index objects. By default no indexes are specified on the attribute.
Your index object can contain the following properties:
Name | Type | Default | Notes |
---|---|---|---|
name | string | ${attribute}${type == "global" ? "GlobalIndex" : "LocalIndex"} | The name of the index. |
type | "global" | "local" | "global" | If the index should be a global index or local index. Attribute will be the hashKey for the index. |
rangeKey | string | undefined | The range key attribute name for a global secondary index. |
project | boolean | [string] | true | Sets the attributes to be projected for the index. true projects all attributes, false projects only the key attributes, and an array of strings projects the attributes listed. |
throughput | number | {read: number, write: number} | undefined | Sets the throughput for the global secondary index. |
If you set index
to true
, it will create an index with all of the default settings.
{
"id": {
"hashKey": true,
"type": String,
},
"email": {
"type": String,
"index": {
"name": "emailIndex",
"global": true
} // creates a global secondary index with the name `emailIndex` and hashKey `email`
}
}
{
"id": {
"hashKey": true,
"type": String,
"index": {
"name": "emailIndex",
"rangeKey": "email",
"throughput": {"read": 5, "write": 10}
} // creates a local secondary index with the name `emailIndex`, hashKey `id`, rangeKey `email`
},
"email": {
"type": String
}
}
hashKey: boolean
You can set this to true to overwrite what the hashKey
for the Model will be. By default the hashKey
will be the first key in the Schema object.
hashKey
is commonly called a partition key
in the AWS documentation.
{
"id": String,
"key": {
"type": String,
"hashKey": true
}
}
rangeKey: boolean
You can set this to true to overwrite what the rangeKey
for the Model will be. By default the rangeKey
won't exist.
rangeKey
is commonly called a sort key
in the AWS documentation.
{
"id": String,
"email": {
"type": String,
"rangeKey": true
}
}
map: string | [string]
This property can be used to use a different attribute name in your internal application as opposed to DynamoDB. This is especially useful if you have a single table design with properties like (pk
& sk
) which don't have much human readable meaning. You can use this to map those attribute names to better human readable names that better represent the underlying data. You can also use it for aliases such as mapping id
to userID
.
When retrieving data from DynamoDB, the attribute will be renamed to this property name, or the first element of the array if it is an array. If you want to change this behavior look at the defaultMap
property.
When saving to DynamoDB, the attribute name will always be used.
"pk": {
"type": String,
"map": "userId"
}
"sk": {
"type": String,
"map": "orderId"
}
"id": {
"type": String,
"map": ["userID", "_id"]
}
alias: string | [string]
This property is the same as map
and used as an alias for that property.
aliases: string | [string]
This property is the same as map
and used as an alias for that property.
defaultMap: string
This property can be set to change the default attribute to be renamed to when retrieving data from DynamoDB. This can either be an element from the map
array or the attribute name.
By default the attribute name will be used if no map
property is set. If a map
property is set, it will use that (or the first element of the array if it is an array).
"id": {
"type": String,
"map": "userID",
"defaultMap": "id"
}
defaultAlias: string
This property is the same as defaultMap
and used as an alias for that property.