    :else    (throw (Exception. (str "Unknown value type: " (type x))))))
 
 (comment (map clj-val->db-val ["foo" 10 3.14 (.getBytes "foo")
-                                #{"a" "b" "c"} #{1 2 3.14} #{(.getBytes "foo")}]))
-
-;;;; TODO Dev
+                               #{"a" "b" "c"} #{1 2 3.14} #{(.getBytes "foo")}]))
+
+;;;; Coercion - objects
+
+(defn- db-map->clj-map [x] (when x (utils/fmap db-val->clj-val (into {} x))))
+
+(defprotocol AsMap (as-map [x]))
+
+(extend-protocol AsMap
+  KeySchemaElement
+  (as-map [element]
+    {:name (.getAttributeName element)
+     :type (-> (.getAttributeType element)
+               (str/lower-case)
+               (keyword))})
+
+  KeySchema
+  (as-map [schema]
+    (merge
+     (when-let [e (.getHashKeyElement schema)]  {:hash-key  (as-map e)})
+     (when-let [e (.getRangeKeyElement schema)] {:range-key (as-map e)})))
+
+  ProvisionedThroughputDescription
+  (as-map [throughput]
+    {:read  (.getReadCapacityUnits throughput)
+     :write (.getWriteCapacityUnits throughput)
+     :last-decrease (.getLastDecreaseDateTime throughput)
+     :last-increase (.getLastIncreaseDateTime throughput)})
+
+  DescribeTableResult
+  (as-map [result]
+    (let [table (.getTable result)]
+      {:name          (.getTableName table)
+       :creation-date (.getCreationDateTime table)
+       :item-count    (.getItemCount table)
+       :key-schema    (as-map (.getKeySchema table))
+       :throughput    (as-map (.getProvisionedThroughput table))
+       :status        (-> (.getTableStatus table)
+                          (str/lower-case)
+                          (keyword))}))
+
+  BatchWriteResponse
+  (as-map [result]
+    {:consumed-capacity-units (.getConsumedCapacityUnits result)})
+
+  BatchWriteItemResult
+  (as-map [result]
+    {:responses         (utils/fmap as-map (into {} (.getResponses result)))
+     :unprocessed-items (.getUnprocessedItems result)})
+
+  BatchResponse
+  (as-map [result]
+    {:consumed-capacity-units (.getConsumedCapacityUnits result)
+     :items (some->> (.getItems result)
+                     (into [])
+                     (utils/fmap #(utils/fmap db-val->clj-val (into {} %))))})
+
+  KeysAndAttributes
+  (as-map [result]
+    (merge
+     (when-let [a (.getAttributesToGet result)] {:attrs (into [] a)})
+     (when-let [c (.getConsistentRead result)]  {:consistent c})
+     (when-let [k (.getKeys result)]            {:keys (utils/fmap as-map (into [] k))})))
+
+  BatchGetItemResult
+  (as-map [result]
+    {:responses        (utils/fmap as-map (into {} (.getResponses result)))
+     :unprocessed-keys (utils/fmap as-map (into {} (.getUnprocessedKeys result)))})
+
+  GetItemResult (as-map [result] (db-map->clj-map (.getItem result)))
+
+  Key
+  (as-map [k]
+    {:hash-key  (db-val->clj-val (.getHashKeyElement k))
+     :range-key (db-val->clj-val (.getRangeKeyElement k))})
+
+  nil (as-map [_] nil))
+
+;;;; API - tables
 
 (defn create-table
   "Create a table in DynamoDB with the given map of properties. The properties

    (result-map
     (.query
      (db-client creds)
      (query-request table hash-key range options)))))

   (db-client creds)
   (DeleteItemRequest. table (item-key {:hash-key hash-key}))))

;;;; API - batch ops

(defn- batch-item-keys [request-or-keys]
  (for [key (:keys request-or-keys request-or-keys)]
