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:

NameTypeDefaultInformation
saveUnknownarray | booleanfalseThis 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 documents 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).
timestampsboolean | objectfalseThis setting lets you indicate to Dynamoose that you would like it to handle storing timestamps in your documents 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. 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.
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"]
}
});

Attribute Types

TypeSet AllowedDynamoDB TypeCustom Dynamoose TypeNested TypeSettingsNotes
StringTrueSFalseFalse
BooleanFalseBOOLFalseFalse
NumberTrueNFalseFalse
BufferTrueBFalseFalse
DateTrueNTrueFalsestorage - miliseconds | seconds (default: miliseconds)Will be stored in DynamoDB as milliseconds since Jan 1 1970, and converted to/from a Date instance.
ObjectFalseMFalseTrue
ArrayFalseLFalseTrue
dynamoose.NULLFalseNULLFalseFalse
SchemaFalseMTrueTrueThis will be converted to an Object type.
ModelOnly if no rangeKey for model's schemaS | N | B | MTrueIf rangeKey in model's schemaModel Types are setup a bit differently. Read below for more information.
CombineFalseSTrueFalseattributes - [string] - The attributes to store in the combine attribute.
seperator - string (default: ,) - The string used to seperate 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 document.
ConstantFalseS | N | BOOLTrueFalsevalue - 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 a document 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, multple 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 documents 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.THIS (to reference your own model).

const userSchema = new dynamoose.Schema({
"id": String,
"name": String,
"parent": dynamoose.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 documents to be a Document 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 document.populate to populate the instances with the documents.

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: miliseconds (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.

{
"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 document. 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 document. If you'd like to force validation to be run every time (even if the attribute doesn't exist in the document) 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 document to be saved if the `isValid` property in the response is true
}
}

required: boolean

You can set an attribute to be required when saving documents 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 a document from DynamoDB. This function will only be run if the item exists in the document. 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 a document to DynamoDB. This function will only be run if the attribute exists in the document. 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 avaiable). Internally Dynamoose uses the document.original() method to access the original value. This means that using Model.batchPut, Model.update or any other document save method that does not have access to document.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. Similarily, 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:

NameTypeDefaultNotes
namestring${attribute}${global ? "GlobalIndex" : "LocalIndex"}Name of index
globalbooleanfalseIf the index should be a global secondary index or not. Attribute will be the hashKey for the index.
rangeKeystringundefinedThe range key attribute name for a global secondary index.
projectboolean | [string]trueSets 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.
throughputnumber | {read: number, write: number}undefinedSets 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
}
}