Schema
Schemas are used to define DynamoDB table attributes and their constraints.
Creating a new Schema
Schemas are created using new Schema(attrDefObj, options)
.
The first argument (attrDefObj
) is an object containing attribute definitions. Keys of this object correspond to attributes in the resulting DynamoDB table. The values of these keys define constraints on those attributes (as well as a few handy features). See Attribute Definitions for a more thorough description.
The second argument (options
) defines options for the table that are beyond the scope of individual attributes. See Schema Options for more.
The following is an example of creating a new Schema:
var Schema = dynamoose.Schema;
var dogSchema = new Schema({
ownerId: {
type: Number,
validate: function(v) { return v > 0; },
hashKey: true
},
name: {
type: String,
rangeKey: true,
index: true // name: nameLocalIndex, ProjectionType: ALL
},
race: {
type: String,
enum: ['Golden retriever', 'Beagle']
},
breed: {
type: String,
trim: true,
required: true,
index: {
global: true,
rangeKey: 'ownerId',
name: 'BreedIndex',
project: true, // ProjectionType: ALL
throughput: 5 // read and write are both 5
}
},
color: {
lowercase: true,
type: [String],
default: ['Brown']
},
age: Number,
awards: {
type: 'list',
list: [{
type: 'map',
map: {
year: Number,
name: String
}
}]
}
},
{
throughput: {read: 15, write: 5}
});
Attribute Types
Attribute Types define the domain of a particular attribute. For example, a name
might be set to String
or age
to Number
.
The following table describes valid Attribute Types, and their translation to DynamoDB types:
Attribute Type | Resulting DynamoDB Type |
---|---|
String | 'S' |
Number | 'N' |
Boolean* | 'S' or 'BOOL' |
Date | 'N' |
Object* | 'S' or 'M' |
Array* | 'S' or 'L' |
Buffer | 'B' |
[String] | 'SS' |
[Number] | 'NS' |
[Boolean] | 'SS' |
[Date] | 'NS' |
[Object] | 'SS' |
[Array] | 'SS' |
* Use useNativeBooleans
and useDocumentTypes
to change DynamoDB type
Attribute Definitions
Attribute definitions define constraints on a particular attribute specified in a Schema. Attribute definitions may be an object type (see Attribute Types) or an object with the following options:
type: AttributeType required
Required for all attribute definitions. Defines the attribute type. See Attribute Types.
hashKey: boolean
Sets the attribute as the table's hash key. If this option isn't specified in a schema, then the first attribute is defined as the hash key.
rangeKey: boolean
Sets the attribute as the table's range key.
required: boolean
Sets the attribute as a 'required' attribute. Required attributes must not be saved as undefined or null, or an error will be thrown.
index: boolean | object | [objects]
Defines the attribute as a local or global secondary index. Index can either be true, an index definition object or and array of index definition objects. The array is used define multiple indexes for a single attribute. The index definition object can contain the following keys:
- name: 'string' - Name of index (Default is
attribute.name + (global ? 'GlobalIndex' : 'LocalIndex')
). - global: boolean - Set the index to be a global secondary index. Attribute will be the hash key for the Index.
- rangeKey: 'string' - The range key for a global secondary index.
- project: boolean | ['string', ...] - Sets the attributes to be projected for the index.
true
projects all attributes,false
projects only the key attributes, and ['string', ...] projects the attributes listed. Default istrue
. - throughput: number | {read: number, write: number} - Sets the throughput for the global secondary index.
default: function | value
Applies a default to the attribute's value when saving, if the value is null or undefined.
If default is a function, the function is called with the current model instance, and the response is assigned to the attribute's value.
If it is a value, the value is simply assigned.
function(model) {
return model.name +'_'+ model.category;
}
Your function may also be an async function with await
statements in it, or you may return a promise. For example:
function(model) {
return new Promise(resolve => {
setTimeout(function() {
resolve("My default value");
}, 1000);
});
}
enum: Array of strings
Force value to be one of the enumeration values.
forceDefault: boolean
(default: false) Will force the default value to always be applied to the attribute event if it already set. This is good for populating data that will be used as sort or secondary indexes.
validate: function, regular expression, object, or value
Validation required before for saving.
If validate is a function, the function is used to validate the attribute's value. The function must have the signature:
function(value, model) {
if(valid)
return true;
else
return false;
}
You can also pass in a function that returns a promise, or an async function:
async function(v) {
const result = await networkRequest(v);
if (result.value === "true") {
return true;
} else {
return false;
}
}
If it is a RegExp
, it is compared using RegExp.test(value)
.
If it is a value, it is compared with ===
.
If an object is passed in, it must have a validator property that is a function. You can set the isAsync
property to true
, to enable callback functionality. If you are using promises, isAsync
is not required. For example:
{
isAsync: true, // default: false
validator: function(v, model, cb) {
setTimeout(function() {
var phoneRegex = /\d{3}-\d{3}-\d{4}/;
var msg = v + ' is not a valid phone number!';
// First argument is a boolean, whether validator succeeded
// 2nd argument is an optional error message override
cb(phoneRegex.test(v), msg);
}, 5);
},
disableModelParameter: false // default: false, if enabled will only pass in value and callback, and not the model
}
Running any Dynamoose method that calls the validate
method indirectly from within the validate
method will cause an infinate loop.
The property name validate
is also an alias for validator
. The property name validator
will take priority if both are passed in.
set: function | object
Adds a setter function that will be used to transform the value before writing to the DB. You can pass in a standard function that returns the new result, an async function that uses await syntax in it, or a function that returns a promise.
You can also pass in an object that defines extra settings and allows for callback based async set. For example:
{
isAsync: true, // default: false
set: function(v, cb) {
setTimeout(function() {
cb("My item: " + v, msg);
}, 5);
}
}
get: function | object
Adds a getter function that will be used to transform the value returned from the DB, fired only if there is a value returned from the DB. You can pass in a standard function that returns the new result, an async function that uses await syntax in it, or a function that returns a promise.
You can also pass in an object that defines extra settings and allows for callback based async get. For example:
{
isAsync: true, // default: false
get: function(v, cb) {
setTimeout(function() {
cb("My item: " + v, msg);
}, 5);
}
}
toDynamo: function | object
Adds a setter function that will directly set the value to the DB. This skips all type management and parsing normally provided by options.set
.
Your function may also be an async function with await statements in it, or return a promise. For example:
function(val) {
return new Promise(resolve => {
setTimeout(function() {
resolve({S: "My custom value"});
}, 1000);
});
}
If an object is passed in it must have a toDynamo
property that is a function. You can set the isAsync
property to true, to enable callback functionality. If you are using promises, isAsync
is not required. For example:
{
isAsync: true, // default: false
toDynamo: function(v, cb) {
setTimeout(function() {
cb({S: "My custom value"});
}, 5);
}
}
fromDynamo: function | object
Adds a getter function that will be used to transform the value directly returned from the DB. This skips all type management and parsing normally provided by options.get
.
Your function may also be an async function with await
statements in it, or you may return a promise. For example:
function(val) {
return new Promise(resolve => {
setTimeout(function() {
resolve("My custom value");
}, 1000);
});
}
If an object is passed in it must have a fromDynamo
property that is a function. You can set the isAsync
property to true
, to enable callback functionality. If you are using promises, isAsync
is not required. For example:
{
isAsync: true, // default: false
fromDynamo: function(v, cb) {
setTimeout(function() {
cb({S: "My custom value"});
}, 5);
}
}
trim: boolean
Trim whitespace from string when saving to DB.
lowercase: boolean
Convert to lowercase when saving to DB.
uppercase: boolean
Convert to uppercase when saving to DB.
Options
throughput: number | string | {read: number, write: number}
Sets the throughput of the DynamoDB table on creation. The value can either be a number or an object with the keys read
and write
(for example: {read: 5, write: 2}
). If it is a number, both read and write are configured to that number. If it is omitted, the read and write values will be set to 1
. Throughput will only be respected on table creation, and will not update the throughput of an existing table.
If this property is set to "ON_DEMAND"
the table will be created using PAY_PER_REQUEST
BillingMode.
var schema = new Schema({...}, {
throughput: 5
});
var schema = new Schema({...}, {
throughput: {
read: 5,
write: 2
}
});
useNativeBooleans: boolean
Store Boolean values as Boolean ('BOOL') in DynamoDB. Default to true
(i.e store as DynamoDB boolean).
var schema = new Schema({...}, {
useNativeBooleans: true
});
useDocumentTypes: boolean
Store Objects and Arrays as Maps ('M') and Lists ('L') types in DynamoDB. Defaults to true
(i.e. store as DynamoDB maps and lists).
var schema = new Schema({...}, {
useDocumentTypes: true
});
timestamps: boolean | {createdAt: string, updatedAt: string}
Defines that schema must contain fields to control creation and last update timestamps. If it is set to true, the fields createdAt
for creation date and updatedAt
for last update will be added to the instance of the model. For example:
var schema = new Schema({...}, {
throughput: 5,
timestamps: true
});
You can specify the names that the fields will use, like in the following example:
var schema = new Schema({...}, {
throughput: 5,
timestamps: {
createdAt: 'creationDate',
updatedAt: 'lastUpdateDate'
}
});
expires: number | {ttl: number, attribute: string, returnExpiredItems: boolean, defaultExpires: function}
Defines that schema must contain an expires attribute. This field is configured in DynamoDB as the TTL attribute. If set to a number
, an attribute named "expires" will be added to the schema. The default value of the attribute will be the current time plus the expires value, unless an optional defaultExpires property is passed in. The expires value is in seconds.
The attribute will be a standard javascript Date
in the object, and will be stored as number ('N') in the DyanmoDB table. The stored number is in seconds. More information about DynamoDB TTL
var schema = new Schema({...}, {
expires: 7*24*60*60 // 1 week in seconds
});
You can specify the attribute name by passing an object:
var schema = new Schema({...}, {
expires: {
ttl: 7*24*60*60, // 1 week in seconds
attribute: 'ttl', // ttl will be used as the attribute name
returnExpiredItems: true, // if expired items will be returned or not (default: true)
defaultExpires: function () { // optional, will default to `ttl` if not defined, this function is mainly used to allow the expires to be set to null or undefined, without resetting to the normal default
return null;
}
}
});
saveUnknown: boolean or array
Specifies that attributes not defined in the schema will be saved and retrieved. This defaults to false.
var schema = new Schema({...}, {
saveUnknown: true
});
If an array is passed in, only attributes that are in the array passed in will be saved and retrieved.
var schema = new Schema({...}, {
saveUnknown: ['name', 'age'] // only `name` and `age` unknown attributes will be saved and retrieved from DynamoDB
});
errorUnknown: boolean
Specifies that any attributes not defined in the schema will throw an error if encountered while parsing records from DynamoDB. This defaults to false.
var schema = new Schema({...}, {
errorUnknown: true
});
attributeToDynamo: function
A function that accepts name, json, model, defaultFormatter, options
.
This will override attribute formatting for all attributes. Whatever is returned by the function will be sent directly to the DB.
var schema = new Schema({...}, {
attributeToDynamo: function(name, json, model, defaultFormatter, options) {
switch(name) {
case 'specialAttribute':
return specialFormatter(json);
default:
return specialFormatter(json);
}
}
});
attributeFromDynamo: function
A function that accepts name, json, fallback
.
This will override attribute parsing for all attributes. Whatever is returned by the function will be passed directly to the model instance.
var schema = new Schema({...}, {
attributeFromDynamo: function(name, json, defaultParser) {
switch(name) {
case 'specialAttribute':
return specialParser(json);
default:
return defaultParser(json);
}
}
});
Methods
You can add custom methods to your Schema. Model methods can be accessed via this.model(modelName)
.
const Schema = dynamoose.Schema;
const dogSchema = new Schema({
ownerId: {
type: Number,
validate: function(v) { return v > 0; },
hashKey: true
},
name: {
type: String,
rangeKey: true,
index: true // name: nameLocalIndex, ProjectionType: ALL
},
friends: {
type: [String],
default: [],
}
});
dogSchema.method('becomeFriends', function (otherDog) {
this.friends = this.friends || [];
otherDog.friends = otherDog.friends || [];
this.friends.push(otherDog.name);
otherDog.friends.push(this.name);
this.model('Dog').batchPut([this, otherDog]);
});
const Dog = dynamoose.model('Dog', dogSchema);
const dog1 = new Dog({ownerId: 137, name: 'Snuffles'});
const dog2 = new Dog({ownerId: 138, name: 'Bill'});
dog1.becomeFriends(dog2);
Static Methods
Can be accessed from the compiled Schema
, similar to how scan()
and query()
are called.
this
will refer to the compiled schema within the definition of the function.
// Construction:
const ModelSchema = new Schema({...})
ModelSchema.statics.getAll = async function (cb) {
let results = await this.scan().exec();
while (results.lastKey) {
results = await this.scan().startKey(results.startKey).exec();
}
return results;
}
const Model = dynamoose.model('Model', ModelSchema)
// Using:
Model.getAll((err, models) => {
models.forEach((model) => {
console.log(model)
});
});
Instance Methods
Can be accessed from a newly created model. this
will refer to the instace of the model within
the definition of the function.
// Construction:
const ModelSchema = new Schema({
name: String
});
ModelSchema.methods.setName = function (name) {
this.name = name
};
const Model = dynamoose.model('Model', ModelSchema);
// Using:
const batman = new Model({name: "Bruce"});
batman.setName("Bob");