

# Get started with DynamoDB Mapper
<a name="ddb-mapper-get-started"></a>

****  
**DynamoDB Mapper is a Developer Preview release. It is not feature complete and is subject to change.**

The following tutorial introduces the basic components of DynamoDB Mapper and shows how to use it in your code.

## Add dependencies
<a name="ddb-mapper-get-started-deps"></a>

To begin working with DynamoDB Mapper in your Gradle project, add a plugin and two dependencies to your `build.gradle.kts` file.

(You can navigate to the *X.Y.Z* link to see the latest version available.)

```
// build.gradle.kts
val sdkVersion: String = [https://github.com/awslabs/aws-sdk-kotlin/releases/latest](https://github.com/awslabs/aws-sdk-kotlin/releases/latest)

plugins {
    id("aws.sdk.kotlin.hll.dynamodbmapper.schema.generator") version "$sdkVersion-beta" // For the Developer Preview, use the beta version of the latest SDK.
}

dependencies {
    implementation("aws.sdk.kotlin:dynamodb-mapper:$sdkVersion-beta")
    implementation("aws.sdk.kotlin:dynamodb-mapper-annotations:$sdkVersion-beta")
}
```

\$1Replace *<Version>* with the latest release of the SDK. To find the latest version of the SDK, check the [latest release on GitHub](https://github.com/awslabs/aws-sdk-kotlin/releases/latest).

**Note**  
Some of these dependencies are optional if you plan to define schemas manually. See [Manually define schemas](ddb-mapper-code-schemas.md) for more information and the reduced set of dependencies.

## Create and use a mapper
<a name="ddb-mapper-get-started-mapper"></a>

DynamoDB Mapper uses the AWS SDK for Kotlin’s DynamoDB client to interact with DynamoDB. You need to provide a fully configured [https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb/aws.sdk.kotlin.services.dynamodb/-dynamo-db-client/index.html](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb/aws.sdk.kotlin.services.dynamodb/-dynamo-db-client/index.html) instance when you create a mapper instance as shown in the following code snippet:

```
import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper
import aws.sdk.kotlin.services.dynamodb.DynamoDbClient

val client = DynamoDbClient.fromEnvironment()
val mapper = DynamoDbMapper(client)
```

**Note**  
`DynamoDbMapper` doesn't support table creation operations. Use the `DynamoDbClient` to create tables.

After you have created the mapper instance, you can use it to get the table instance as shown next:

```
val carsTable = mapper.getTable("cars", CarSchema)
```

The previous code gets a reference to a table in `DynamoDB` named `cars` with a schema defined by `CarSchema` (we discuss schemas below). After you create a table instance, you can perform operations against it. The following code snippet show two example operations against the `cars` table:

```
carsTable.putItem {
    item = Car(make = "Ford", model = "Model T", ...)
}

carsTable
   .queryPaginated {
        keyCondition = KeyFilter(partitionKey = "Peugeot")
   }
   .items()
   .collect { car -> println(car) }
```

The previous code creates a new item in the `cars` table. The code creates a `Car` instance inline using the `Car` class, whose definition is shown below. Next, the code queries the `cars` table for items whose partition key is `Peugeot` and prints them. Operations are [described in more detail below](#ddb-mapper-gs-invoke-ops).

## Define a schema with class annotations
<a name="ddb-mapper-gs-anno-schema-def"></a>

For a variety of Kotlin classes, the SDK can automatically generate schemas at build time by using the DynamoDB Mapper Schema Generator plugin for Gradle. When you use the schema generator, the SDK inspects your classes to infer the schema, which alleviates some of the boilerplate involved in manually defining schemas. You can customize the schema that is generated by using additional [annotations](ddb-mapper-anno-schema-gen.md#ddb-mapper-anno-schema-gen-annotate) and [configuration](ddb-mapper-anno-schema-gen.md#ddb-mapper-anno-schema-gen-conf-plugin).

To generate a schema from annotations, first annotate your classes with `@[DynamoDbItem](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb-mapper-annotations/aws.sdk.kotlin.hll.dynamodbmapper/-dynamo-db-item/index.html)` and any keys with `@[DynamoDbPartitionKey](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb-mapper-annotations/aws.sdk.kotlin.hll.dynamodbmapper/-dynamo-db-partition-key/index.html)` and `@[DynamoDbSortKey](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb-mapper-annotations/aws.sdk.kotlin.hll.dynamodbmapper/-dynamo-db-sort-key/index.html)`. The following code shows the annotated `Car` class:

```
// The annotations used in the Car class are used by the plugin to generate a schema.
@DynamoDbItem
data class Car(
    @DynamoDbPartitionKey
    val make: String,
    
    @DynamoDbSortKey
    val model: String,
    
    val initialYear: Int
)
```

After building, you can refer to the automatically generated `CarSchema`. You can use the reference in the mapper's `getTable` method to get a table instance as shown in the following:

```
import aws.sdk.kotlin.hll.dynamodbmapper.generatedschemas.CarSchema

// `CarSchema` is generated at build time.
val carsTable = mapper.getTable("cars", CarSchema)
```

Alternatively, you can get the table instance by taking advantage of an extension method on `[DynamoDbMapper](https://docs.aws.amazon.com/sdk-for-kotlin/api/latest/dynamodb-mapper/aws.sdk.kotlin.hll.dynamodbmapper/-dynamo-db-mapper/index.html)` that is automatically generated at build time. By using this approach, you don't need to refer to the schema by name. As shown in the following, the automatically generated `getCarsTable` extension method returns a reference to the table instance:

```
val carsTable = mapper.getCarsTable("cars")
```

See [Generate a schema from annotations](ddb-mapper-anno-schema-gen.md) for more details and examples.

## Invoke operations
<a name="ddb-mapper-gs-invoke-ops"></a>

DynamoDB Mapper supports a subset of the operations available on the SDK's `DynamoDbClient`. Mapper operations are named the same as their counterparts on the SDK client. Many mapper request/response members are the same as their SDK client counterparts, although some have been renamed, re-typed, or dropped altogether.

You invoke an operation on a table instance using a DSL syntax as shown in the following:

```
import aws.sdk.kotlin.hll.dynamodbmapper.operations.putItem
import aws.sdk.kotlin.services.dynamodb.model.ReturnConsumedCapacity

val putResponse = carsTable.putItem {
    item = Car(make = "Ford", model = "Model T", ...)
    returnConsumedCapacity = ReturnConsumedCapacity.Total
}

println(putResponse.consumedCapacity)
```

You can also invoke an operation by using an explicit request object:

```
import aws.sdk.kotlin.hll.dynamodbmapper.operations.PutItemRequest
import aws.sdk.kotlin.services.dynamodb.model.ReturnConsumedCapacity

val putRequest = PutItemRequest<Car> {
    item = Car(make = "Ford", model = "Model T", ...)
    returnConsumedCapacity = ReturnConsumedCapacity.Total
}

val putResponse = carsTable.putItem(putRequest)
println(putResponse.consumedCapacity)
```

The previous two code examples are equivalent.

### Work with paginated responses
<a name="ddb-mapper-gs-pagination"></a>

Some operations like `query` and `scan` can return data collections that might be too large to return in a single response. To ensure that all objects are processed, DynamoDB Mapper provides paginating methods, which do not call DynamoDB immediately, but instead return a `[Flow](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)` of the operation response type, such as `Flow<ScanResponse<Car>>` shown in the following:

```
import aws.sdk.kotlin.hll.dynamodbmapper.operations.scanPaginated

val scanResponseFlow = carsTable.scanPaginated { }

scanResponseFlow.collect { response ->
    val items = response.items.orEmpty()
    println("Found page with ${items.size} items:")
    
    items.forEach { car -> println(car) }
}
```

Often, a flow of objects is more useful to business logic than a flow of responses *containing* objects. The mapper provides an extension method on paginated responses to access the flow of objects. For example, the following code returns a `Flow<Car>` rather than a `Flow<ScanResponse<Car>>` as shown previously:

```
import aws.sdk.kotlin.hll.dynamodbmapper.operations.items
import aws.sdk.kotlin.hll.dynamodbmapper.operations.scanPaginated

val carFlow = carsTable
    .scanPaginated { }
    .items()

carFlow.collect { car -> println(car) }
```