ExAws v0.4.19 ExAws.Dynamo.Client behaviour

Defines a Dynamo Client

By default you can use ExAws.Dynamo

NOTE: When Mix.env in [:test, :dev] dynamo clients will run by default against Dynamodb local.

Basic usage

defmodule User do
  @derive [ExAws.Dynamo.Encodable]
  defstruct [:email, :name, :age, :admin]
end

alias ExAws.Dynamo

# Create a users table with a primary key of email [String]
# and 1 unit of read and write capacity
Dynamo.create_table("Users", "email", %{email: :string}, 1, 1)

user = %User{email: "bubba@foo.com", name: "Bubba", age: 23, admin: false}
# Save the user
Dynamo.put_item("Users", user)

# Retrieve the user by email and decode it as a User struct.
result = Dynamo.get_item!("Users", %{email: user.email})
|> Dynamo.Decoder.decode(as: User)

assert user == result

Customization

If you want more than one client you can define your own as follows. If you don’t need more than one or plan on customizing the request process it’s generally easier to just use and configure the ExAws.Dynamo client.

defmodule MyApp.Dynamo do
  use ExAws.Dynamo.Client, otp_app: :my_otp_app
end

In your config

config :my_otp_app, :ex_aws,
  dynamodb: [], # Dynamo config goes here

You can now use MyApp.Dynamo as the root module for the Dynamo api without needing to pass in a particular configuration. This enables different otp apps to configure their AWS configuration separately.

The alignment with a particular OTP app while convenient is however entirely optional. The following also works:

defmodule MyApp.Dynamo do
  use ExAws.Dynamo.Client

  def config_root do
    Application.get_all_env(:my_aws_config_root)
  end
end

ExAws now expects the config for that dynamo client to live under

config :my_aws_config_root
  dynamodb: [] # Dynamo config goes here

Default config values can be found in ExAws.Config.

General notes

All options are handled as underscored atoms instead of camelcased binaries as specified in the Dynamo API. IE IndexName would be :index_name. Anywhere in the API that requires dynamo type annotation ({"S":"mystring"}) is handled for you automatically. IE

ExAws.Dynamo.scan("Users", expression_attribute_values: [api_key: "foo"])

Transforms into a query of

%{"ExpressionAttributeValues" => %{api_key: %{"S" => "foo"}}, "TableName" => "Users"}

Consult the function documentation to see precisely which options are handled this way.

If you wish to avoid this kind of automatic behaviour you are free to specify the types yourself. IE:

ExAws.Dynamo.scan("Users", expression_attribute_values: [api_key: %{"B" => "Treated as binary"}])

Becomes:

%{"ExpressionAttributeValues" => %{api_key: %{"B" => "Treated as binary"}}, "TableName" => "Users"}

Alternatively, if what’s being encoded is a struct, you’re always free to implement ExAws.Dynamo.Encodable for that struct.

Examples

defmodule User do
  @derive [ExAws.Dynamo.Encodable]
  defstruct [:email, :name, :age, :admin]
end

alias ExAws.Dynamo

# Create a users table with a primary key of email [String]
# and 1 unit of read and write capacity
Dynamo.create_table("Users", "email", %{email: :string}, 1, 1)

user = %User{email: "bubba@foo.com", name: "Bubba", age: 23, admin: false}
# Save the user
Dynamo.put_item("Users", user)

# Retrieve the user by email and decode it as a User struct.
result = Dynamo.get_item!("Users", %{email: user.email})
|> Dynamo.Decoder.decode(as: User)

assert user == result

http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations.html

Summary

Types

batch_get_item_opts :: [{:return_consumed_capacity, return_consumed_capacity_vals}]
batch_write_item_opts :: [return_consumed_capacity: return_consumed_capacity_vals, return_item_collection_metrics: return_item_collection_metrics_vals]
delete_item_opts :: [condition_expression: binary, expression_attribute_names: expression_attribute_names_vals, expression_attribute_values: expression_attribute_values_vals, return_consumed_capacity: return_consumed_capacity_vals, return_item_collection_metrics: return_item_collection_metrics_vals, return_values: return_values_vals]
dynamo_type_names ::
  :blob |
  :boolean |
  :blob_set |
  :list |
  :map |
  :number_set |
  :null |
  :number |
  :string |
  :string_set
exclusive_start_key_vals ::
  [{atom, binary}] |
  %{atom => binary}
expression_attribute_names_vals :: %{binary => binary}
expression_attribute_values_vals ::
  [{atom, binary}] |
  %{atom => binary}
get_item :: [consistent_read: boolean, keys: [primary_key]]
get_item_opts :: [consistent_read: boolean, expression_attribute_names: expression_attribute_names_vals, projection_expression: binary, return_consumed_capacity: return_consumed_capacity_vals]
key_definitions :: [{atom | binary, dynamo_type_names}, ...]
key_schema :: [{atom | binary, :hash | :range}, ...]
primary_key ::
  [{atom, binary}] |
  %{atom => binary}
put_item_opts :: [condition_expression: binary, expression_attribute_names: expression_attribute_names_vals, expression_attribute_values: expression_attribute_values_vals, return_consumed_capacity: return_consumed_capacity_vals, return_item_collection_metrics: return_item_collection_metrics_vals, return_values: return_values_vals]
query_opts :: [consistent_read: boolean, exclusive_start_key: exclusive_start_key_vals, expression_attribute_names: expression_attribute_names_vals, expression_attribute_values: expression_attribute_values_vals, filter_expression: binary, index_name: binary, key_condition_expression: binary, limit: pos_integer, projection_expression: binary, return_consumed_capacity: return_consumed_capacity_vals, scan_index_forward: boolean, select: select_vals]
return_consumed_capacity_vals ::
  :none |
  :total |
  :indexes
return_values_vals ::
  :none |
  :all_old |
  :updated_old |
  :all_new |
  :updated_new
scan_opts :: [exclusive_start_key: exclusive_start_key_vals, expression_attribute_names: expression_attribute_names_vals, expression_attribute_values: expression_attribute_values_vals, filter_expression: binary, index_name: binary, limit: pos_integer, projection_expression: binary, return_consumed_capacity: return_consumed_capacity_vals, segment: non_neg_integer, select: select_vals, total_segments: pos_integer]
select_vals ::
  :all_attributes |
  :all_projected_attributes |
  :specific_attributes |
  :count
table_name :: binary
update_item_opts :: [condition_expression: binary, expression_attribute_names: expression_attribute_names_vals, expression_attribute_values: expression_attribute_values_vals, return_consumed_capacity: return_consumed_capacity_vals, return_item_collection_metrics: return_item_collection_metrics_vals, return_values: return_values_vals, update_expression: binary]
write_item :: [[{:delete_request, [{:key, primary_key}]}] | [{:put_request, [{:item, %{}}]}]]

Callbacks

batch_get_item(%{})

Specs

batch_get_item(%{table_name => get_item}) :: ExAws.Dynamo.Request.response_t

Get up to 100 items (16mb)

Map of table names to request parameter maps. http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html

Parameters with keys that are automatically annotated with dynamo types are: [:keys]

Dynamo.batch_get_item(%{
  "Users" => [
    consistent_read: true,
    keys: [
      [api_key: "key1"],
      [api_key: "api_key2"]
    ]
  ],
  "Subscriptions" => %{
    keys: [
      %{id: "id1"}
    ]
  }
})

As you see you’re largely free to use either keyword args or maps in the body. A map is required for the argument itself because the table names are most often binaries, and I refuse to inflict proplists on anyone.

batch_get_item(%{}, opts)

Specs

batch_get_item(%{table_name => get_item}, opts :: batch_get_item_opts) :: ExAws.Dynamo.Request.response_t
batch_write_item(%{})

Specs

batch_write_item(%{table_name => [write_item]}) :: ExAws.Dynamo.Request.response_t

Put or delete up to 25 items (16mb)

Map of table names to request parameter maps. http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html

Parameters with keys that are automatically annotated with dynamo types are: [:keys]

batch_write_item(%{}, opts)

Specs

batch_write_item(%{table_name => [write_item]}, opts :: batch_write_item_opts) :: ExAws.Dynamo.Request.response_t
config_root()

Specs

config_root :: Keyword.t

Retrieves the root AWS config for this client

create_table(table_name, key_schema, key_definitions, read_capacity, write_capacity)

Specs

create_table(table_name :: binary, key_schema :: key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer) :: ExAws.Dynamo.Request.response_t
create_table(table_name :: binary, key_schema :: binary | atom | key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer) :: ExAws.Dynamo.Request.response_t

Create table

key_schema allows specifying hash and / or range keys IE

[api_key: :hash, something_rangy: :range]
create_table(table_name, key_schema, key_definitions, read_capacity, write_capacity, global_indexes, local_indexes)

Specs

create_table(table_name :: binary, key_schema :: key_schema, key_definitions :: key_definitions, read_capacity :: pos_integer, write_capacity :: pos_integer, global_indexes :: [Map.t], local_indexes :: [Map.t]) :: ExAws.Request.response_t

Create table with secondary indices

Each index should follow the format outlined here: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html

For convenience, the keys in each index map are allowed to be atoms. IE: "KeySchema" in the aws docs can be key_schema:

Note that both the global_indexes and local_indexes arguments expect a list of such indices.

Examples

secondary_index = [%{
  index_name: "my-global-index",
  key_schema: [%{
    attribute_name: "email",
    attribute_type: "HASH",
  }],
  provisioned_throughput: %{
    read_capacity_units: 1,
    write_capacity_units: 1,
  },
  projection: %{
    projection_type: "KEYS_ONLY",
  }
}]
create_table("TestUsers", [id: :hash], %{id: :string}, 1, 1, secondary_index, [])
delete_item(table_name, primary_key)

Specs

delete_item(table_name :: table_name, primary_key :: primary_key) :: ExAws.Dynamo.Request.response_t

Delete item in table

delete_item(table_name, primary_key, opts)

Specs

delete_item(table_name :: table_name, primary_key :: primary_key, opts :: delete_item_opts) :: ExAws.Dynamo.Request.response_t
delete_table(table)

Specs

delete_table(table :: binary) :: ExAws.Dynamo.Request.response_t

Delete Table

describe_table(name)

Specs

describe_table(name :: binary) :: ExAws.Dynamo.Request.response_t

Describe table

get_item(table_name, primary_key)

Specs

get_item(table_name :: table_name, primary_key :: primary_key) :: ExAws.Dynamo.Request.response_t

Get item from table

get_item(table_name, primary_key, opts)

Specs

get_item(table_name :: table_name, primary_key :: primary_key, opts :: get_item_opts) :: ExAws.Dynamo.Request.response_t
get_item!(table_name, primary_key)

Specs

get_item!(table_name :: table_name, primary_key :: primary_key) :: %{}

Get an item from a dynamo table, and raise if it does not exist or there is an error

get_item!(table_name, primary_key, opts)

Specs

get_item!(table_name :: table_name, primary_key :: primary_key, opts :: get_item_opts) :: %{}
list_tables()

Specs

list_tables :: ExAws.Dynamo.Request.response_t

List tables

put_item(table_name, record)

Specs

put_item(table_name :: table_name, record :: %{}) :: ExAws.Dynamo.Request.response_t

Put item in table

put_item(table_name, record, opts)

Specs

put_item(table_name :: table_name, record :: %{}, opts :: put_item_opts) :: ExAws.Dynamo.Request.response_t
query(table_name)

Specs

query(table_name :: table_name) :: ExAws.Dynamo.Request.response_t

Query Table

Please read: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html

Dynamo.query("Users",
  limit: 1,
  expression_attribute_values: [desired_api_key: "adminkey"],
  key_condition_expression: "api_key = :desired_api_key")

Parameters with keys that are automatically annotated with dynamo types are: [:exclusive_start_key, :expression_attribute_names]

query(table_name, opts)

Specs

query(table_name :: table_name, opts :: query_opts) :: ExAws.Dynamo.Request.response_t
request(client_struct, action, data)

Specs

request(client_struct :: %{}, action :: atom, data :: %{}) :: term

Enables custom request handling.

By default this just forwards the request to the ExAws.Dynamo.Request.request/3. However, this can be overriden in your client to provide pre-request adjustments to headers, params, etc.

scan(table_name)

Specs

scan(table_name :: table_name) :: ExAws.Dynamo.Request.response_t

Scan table

Please read http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html

Dynamo.scan("Users"
  limit: 1,
  expression_attribute_values: [desired_api_key: "adminkey"],
  expression_attribute_names: %{"#asdf" => "api_key"},
  filter_expression: "#asdf = :desired_api_key")

Generally speaking you won’t need to use :expression_attribute_names. It exists to alias a column name if one of the columns you want to search against is a reserved dynamo word, like Percentile. In this case it’s totally unnecessary as api_key is not a reserved word.

Parameters with keys that are automatically annotated with dynamo types are: [:exclusive_start_key, :expression_attribute_names]

scan(table_name, opts)

Specs

scan(table_name :: table_name, opts :: scan_opts) :: ExAws.Dynamo.Request.response_t
stream_query(table_name)

Specs

stream_query(table_name :: table_name) :: Enumerable.t

Stream records from table

Returns an enumerable which handles pagination automatically in the backend.

Dynamo.stream_query("Users", filter_expression: "api_key = :api_key", expression_attribute_values: [api_key: "api_key_i_want"])
|> Enum.to_list #=> Returns every item in the Users table with an api_key == "api_key_i_want".
stream_query(table_name, opts)

Specs

stream_query(table_name :: table_name, opts :: query_opts) :: Enumerable.t
stream_scan(table_name)

Specs

stream_scan(table_name :: table_name) :: Enumerable.t

Stream records from table

Same as scan/1,2 but the records are a stream which will automatically handle pagination

Dynamo.stream_scan("Users")
|> Enum.to_list #=> Returns every item in the Users table.
stream_scan(table_name, opts)

Specs

stream_scan(table_name :: table_name, opts :: scan_opts) :: Enumerable.t
update_item(table_name, primary_key, opts)

Specs

update_item(table_name :: table_name, primary_key :: primary_key, opts :: update_item_opts) :: ExAws.Dynamo.Request.response_t

Update item in table

For update_args format see http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html

update_table(name, attributes)

Specs

update_table(name :: binary, attributes :: Keyword.t) :: ExAws.Dynamo.Request.response_t

Update Table