View a markdown version of this page

Configure DynamoDB Mapper - AWS SDK for Kotlin

Configure DynamoDB Mapper

You configure a DynamoDbMapper by passing a configuration block when you create it. The primary configuration mechanism is interceptors, which are objects that hook into the mapper’s request pipeline to observe or modify operations as they run.

import aws.sdk.kotlin.hll.dynamodbmapper.DynamoDbMapper val mapper = DynamoDbMapper(client) { interceptors += LoggingInterceptor() }

The mapper’s built-in features (TTL and atomic counters) are themselves implemented as interceptors that are registered by default, so adding your own with += augments that default set rather than replacing it.

The request pipeline

Every mapper operation flows through a five-step pipeline:

  1. Initialization: set up the operation and gather initial context.

  2. Serialization: convert the high-level request (and your objects) into a low-level request (and DynamoDB items).

  3. Low-level invocation: call the underlying DynamoDbClient.

  4. Deserialization: convert the low-level response (and DynamoDB items) into a high-level response (and your objects).

  5. Completion: finalize the high-level response or exception to return to the caller.

Hooks

An interceptor exposes hooks that run before or after pipeline steps. Hooks come in two kinds:

  • Read-only hooks observe an operation without changing it. They are useful for logging, metrics, and debugging. They run before and after each step and return nothing.

  • Modify hooks can change the request or response in flight. They run before each step and return the (possibly modified) value.

The following table lists the hooks in pipeline execution order:

Hook Kind Runs

readAfterInitialization

read-only

after Initialization

modifyBeforeSerialization

modify

before Serialization

readBeforeSerialization

read-only

before Serialization

readAfterSerialization

read-only

after Serialization

modifyBeforeInvocation

modify

before Low-level invocation

readBeforeInvocation

read-only

before Low-level invocation

readAfterInvocation

read-only

after Low-level invocation

modifyBeforeDeserialization

modify

before Deserialization

readBeforeDeserialization

read-only

before Deserialization

readAfterDeserialization

read-only

after Deserialization

modifyBeforeCompletion

modify

before Completion

readBeforeCompletion

read-only

before Completion

All hooks have default no-op implementations, so an interceptor overrides only the hooks it needs.

Execution order

When multiple interceptors are registered, the order they appear in interceptors determines execution order:

  • For phases before the low-level invocation, hooks run in registration order.

  • For phases after the low-level invocation, hooks run in reverse registration order.

This mirrors a nested call stack: each interceptor "wraps" the ones registered after it.

Write an interceptor

Implement InterceptorAny (a type alias for an interceptor over any item, request, and response types) and override the hooks you care about. This read-only interceptor logs each operation as it enters the pipeline and just before the service call:

import aws.sdk.kotlin.hll.dynamodbmapper.items.ItemSchema import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.HReqContext import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.InterceptorAny import aws.sdk.kotlin.hll.dynamodbmapper.pipeline.LReqContext class LoggingInterceptor : InterceptorAny { override fun readAfterInitialization(ctx: HReqContext<Any, ItemSchema<Any>, Any>) { println("Starting operation for request: ${ctx.highLevelRequest}") } override fun readBeforeInvocation(ctx: LReqContext<Any, ItemSchema<Any>, Any, Any>) { println("Calling DynamoDB: ${ctx.lowLevelRequest}") } }

Each hook receives a context object that exposes the state available at that point in the pipeline. For example, highLevelRequest is available early on, and lowLevelRequest is available once serialization has produced it. A modify hook returns the value it wants to pass downstream (the relevant part of the context), while a read-only hook returns nothing.

Register interceptors

Add interceptors in the configuration block when creating the mapper:

val mapper = DynamoDbMapper(client) { interceptors += LoggingInterceptor() }

Using += preserves the default interceptors that power TTL and atomic counters. Assigning the interceptors list outright replaces them, which disables those built-in features. Prefer += unless you intend to remove the defaults.