Recommend storing these credentials in environment variables and loading them
in a config file.
So rather than the text strings appearing in your code (and worse, getting
committed to your repo!), set them as environment variables and load them
in your code as follows:
Can optionally specify a region. If none is specified, us-east-1 is the default.
For example, to use the eu-west-1 region based in Ireland, use the following
Only this short string is necessary to specify the region, Dynasty will convert
it into the full endpoint URL for you.
Ok, so now that we’ve got it installed and we have the credentials sorted, let’s
instantiate it so we can actually use it!
Optionally, provide a url as the second argument to connect to a Dynamo
instance that is not on AWS. For example if you are running Dynamo
you would instantiate it as follows:
Note, you still have to provide credentials, even if running locally doesn’t
require them because the internal code expects them to be present.
That was easy! But how to query it?
But first, a bit of background…
This is not a full DynamoDB primer, but it makes some sense to give a high
level overview for the rest of this document to make sense.
Ideally, you should never have to visit the underlying DynamoDB docs in order
to use it, Dynasty should provide everything you need. So this is not
exhaustive, but should be enough to get you going.
DynamoDB stores items in Tables. A Table in Dynamo is
analogous to a Table in SQL or a Collection in Mongo.
Though of course unlike a SQL table, DynamoDB is NoSQL so there is no Schema here.
It is the fundamental bucket into which items are pushed.
Each Table has a name and a Key Schema (explained below).
If you wanted to store data on a series of Users, it may make sense to create
a UserData table to hold the data for each user.
Items are put into Tables.
They are analogous to rows in SQL or documents in Mongo.
Each Item must at minimum have the keys/values specified in the Key Schema
but they may have any number of other key/value pairs.
Aside from the Key Schema, there is no need to be consistent across items.
For user with username victorquinn you may want an item that looks like:
whereas for someone else:
Note how one has motorcycle, the other has car, one has a last name, the other
doesn’t, this is fine as long as they both meet the Key Schema.
DynamoDB allows 2 different Key Schemas:
Hash Type Primary Key
Hash and Range Type Primary Key
Hash Type Primary Key
Should be used when you have a single unique identifier used in the Table to
look up the Item.
It’s better to use a Hash and Range Type Primary Key when possible (as it can
be better on performance, more below) but if you have only a single identifier
a Hash Type Primary Key is perfect.
Example: A table of UserData, the username may be a Hash Type Primary Key.
Given this username you can uniquely identify the user and retrieve their
Hash and Range Type Primary Key
A composite key. Made up of 2 keys, the so called Hash and Range keys.
This can be more performant as Amazon wil shard the database based on the Hash
key making lookups in very large databases faster. (thankfully you never have to
worry about any of this, it all happens magically in the background)
Example: A table of certifications of User by state. Hash key could be State,
Range key could be Username. This will help as rather than looking through every
record for the State/Username combo, DynamoDB can quickly narrow it down by
So assuming you’ve created a table in DynamoDB (either programmatically with
Dynasty or manually in the AWS console) we can get an object representing
that Table to put items into that table, get items from that table, and
perform any other operations on that table.
So let’s say we have a DynamoDB table already created called UserData and we
want to retrieve an item from it.
Now we can query it:
Note, I’ve chosen to use a Mongo-style syntax here, rather than the DynamoDB
names. This is because:
I find it more natural
Other developers coming to Dynamo will probably find it more natural
If moving to/from DynamoDB this should ease the transition quite a bit.
Also note, we’re not passing it a callback. We can, but we don’t need to because
Dynasty has promise support baked in!
Dynasty will also accept a callback function as an optional argument though!
As shown above, we performed a query and set its return to a variable we called
promise. Of course we could have called the variable anything, but we called
it that for clarity because that thing it returned was a Promise.
In brief Promises are a way clean up the callback stack and provide a way for programs to
pass the flow off as it sees fit rather than trying to jam it all into a single
callback function. It offers an inversion of control so the code requesting the
asyncronous operation need not relinquish it.
In more concrete terms, a Promise is an object that has callbacks which will
be called when the asynchronous action finishes.
Those callbacks do not need to be set or determined at the time the Promise is
formed, so they can be added on later.
This means that a program can ask for something that will take time (such as
going off into the intarwebs to hit DynamoDB and return a result) and from that
request receive in return a Promise to which it can attach callbacks which
will be called when the network call returns and the object is ready for use.
Real World Counter-example - Restaurant Pager
For something like this which is rather complex, I prefer a real world tangible
example, or in this case a counter-example.
What follows is an example of a Callback and not a promise.
Imagine one of those flashy pagers that some restaurants will give you
when they have a long wait for a table.
You no longer have to stand and wait in a line, you can take that pager, go do
something else, and eventually the pager will flash and vibrate you to alert you
that your table is ready.
So this is already better than the blocking request most other systems use
exclusively to handle the time spent waiting for a response.
This pager is the more analogous to a Callback in Node. You had to
tell the maître d’ up front that you wanted a table and for how many and all
The restaurant in this situation holds the control. You have to give them
your request up front and when they’re ready they call you and you get what
you asked for at the time they gave you the pager. If you changed your mind
between the time you were handed the pager and the time you’re called you’re
out of luck.
But what if you wanted to invert that control?
What if you could say to them, I will want something when you can handle me,
I’m not sure exactly what I’ll want, but I can take my time figuring it out
because you’re busy anyway.
Real World Example - Deli
For a real world example of a Promise, think of a deli.
In a deli, you grab a slip of paper which represents
a Promise that when they have capacity, one of the workers will serve you.
You don’t have to tell them what you want up front. You could change your mind
in the time between when you pulled that number, or you could ignore it
completely when the number came up, or you could pass it off to a friend who
could do what they’d like when the number comes up.
noticeable but there are many and a lot of people are really excited about
You can certainly count me, the author of this library, among those who are
Optionally specify the name of a table to start the list. This is useful for
If you had previously done a list() command and there were more tables
than a response can handle, you would have received a LastEvaluatedTableName
with the response which was the last table it could return. Pass this back in
a subsequent request to start the list where you left off.
Optionally specify a limit which is the max number of table names to return.
Useful for paging.
Optionally specify a callback function with or without other arguments.
These methods act on Items within a Table.
As such, for all the following examples, we’ll assume:
That the setup has been, so there’s a dynasty object with the credentials
That the tables we’re accessing have already been created, unless otherwise
That there is a table object already created for each table
In other words, we’re going to assume the following code has been run already:
We will use lands and counties as examples below.
This top-level overview is to help cut down on the repetition below.
Scans and returns all items in a table as an array of objects.
You can optionally pass in the LastEvaluatedKey from a previously executed scan
operation as the ExclusiveStartKey to implement paginated scans (when you have
more than 1 MB of date and can’t get it all in one go).