UNPKG

@nerdware/ddb-single-table

Version:

A schema-based DynamoDB modeling tool, high-level API, and type-generator built to supercharge single-table designs!⚡

300 lines 17.7 kB
import { DdbClientWrapper } from "../DdbClientWrapper/index.js"; import type { ModelConstructorParameters, AttributesAliasesMap, KeyParameters, GetItemOpts, BatchGetItemsOpts, CreateItemOpts, UpsertItemOpts, UpdateItemOpts, DeleteItemOpts, BatchWriteItemsOpts, QueryOpts, ScanOpts } from "./types/index.js"; import type { IODirection, EnabledIOActions } from "../IOActions/types/index.js"; import type { ModelSchemaType, ModelSchemaOptions, ModelSchemaEntries } from "../Schema/types/index.js"; import type { TableKeysAndIndexes } from "../Table/types/index.js"; import type { BaseItem, UnknownItem, ItemTypeFromSchema, ItemCreationParameters } from "../types/index.js"; import type { PartialDeep } from "type-fest"; /** * Each Model instance is provided with CRUD methods featuring parameter and return types which * reflect the Model's schema. Model methods wrap `DynamoDBClient` command operations with sets * of schema-aware middleware called {@link ioActions|"IO-Actions"} which provide rich functionality * for database-IO like alias mapping, value validation, user-defined transforms, etc. * * IO-Actions are grouped into two sets based on the request-response cycle: * - **`toDB`**: IO-Actions performed on _request arguments_. * - **`fromDB`**: IO-Actions performed on _response values_. * * The IO-Actions undertaken for each set are listed below in order of execution. Note that some * IO-Actions are skipped by certain methods, depending on the method's purpose. For example, item * values provided to `Model.updateItem` are not subjected to `"required"` checks, since the method * is intended to update individual properties of existing items. * _See **{@link ioActions|IO-Actions}** for more info an any of the IO-Actions listed below._ * * **`toDB`**: * 1. **`Alias Mapping`** — Replaces "alias" keys with attribute names. * 2. **`Set Defaults`** — Applies defaults defined in the schema. * 3. **`Attribute toDB Modifiers`** — Runs your `transformValue.toDB` fns. * 4. **`Item toDB Modifier`** — Runs your `transformItem.toDB` fn. * 5. **`Type Checking`** — Checks properties for conformance with their `"type"`. * 6. **`Attribute Validation`** — Validates individual item properties. * 7. **`Item Validation`** — Validates an item in its entirety. * 8. **`"Required" Checks`** — Checks for `"required"` and `"nullable"` attributes. * * **`fromDB`**: * 1. **`Attribute fromDB Modifiers`** — Runs your `transformValue.fromDB` fns. * 2. **`Item fromDB Modifier`** — Runs your `transformItem.fromDB` fn. * 3. **`Alias Mapping`** — Replaces attribute names with "alias" keys. * * #### Ordering of Attributes * IO-Actions which process individual attributes always process attributes in the same order: * 1. The table hash key is always processed first. * 2. The table sort key is always processed second. * 3. Any index PKs are processed after the table SK. * 4. All other attributes are then processed in the order they are defined in the schema. * * Aside from ensuring predictable execution, this consistency also opens up design opportunities * for your schema. For example, if you have a schema which uses a function to dynamically generate * a default value for an `id` attribute which is used as the table hash key, other non-key * attributes may be defined using the item's generated `id` value. * * @template Schema - The Model's readonly schema. * @template ItemType - A type which reflects a complete instance of a Model item. * @template ItemCreationParams - The parameters used to create a new item instance. */ export declare class Model<const Schema extends ModelSchemaType, ItemType extends UnknownItem = ItemTypeFromSchema<Schema>, ItemCreationParams extends UnknownItem = ItemCreationParameters<Schema>> implements TableKeysAndIndexes { readonly modelName: string; readonly schema: Schema; readonly schemaEntries: ModelSchemaEntries; readonly schemaOptions: ModelSchemaOptions; readonly attributesToAliasesMap: AttributesAliasesMap; readonly aliasesToAttributesMap: AttributesAliasesMap; readonly tableName: string; readonly tableHashKey: TableKeysAndIndexes["tableHashKey"]; readonly tableRangeKey?: TableKeysAndIndexes["tableRangeKey"]; readonly indexes?: TableKeysAndIndexes["indexes"]; /** A wrapper-class around the DynamoDB client instance which greatly simplifies DDB operations. */ readonly ddb: DdbClientWrapper; constructor( /** The name of the Model. */ modelName: string, /** The Model's {@link Schema}. */ modelSchema: Schema, /** {@link ModelSchemaOptions} and table key/index properties. */ { tableName, tableHashKey, tableRangeKey, indexes, ddb: ddbClientWrapper, autoAddTimestamps, allowUnknownAttributes, transformItem, validateItem, }: ModelConstructorParameters); /** * [`GetItem`][api-ref] operation wrapper. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html * * @param primaryKeys The primary keys of the item to get. * @param getItemOpts Options for the underlying `GetItem` operation. * @returns The item, if found. */ readonly getItem: (primaryKeys: KeyParameters<Schema>, getItemOpts?: GetItemOpts) => Promise<ItemType | undefined>; /** * [`BatchGetItem`][api-ref] operation wrapper. * * - **Max Chunk Size**: The provided `primaryKeys` are spliced into chunks of 100 (the maximum * limit set by AWS for BatchGetItem requests). * * - **Automatic Retries**: Per AWS recommendations, BatchGetItem requests which either return * `UnprocessedKeys` or result in a retryable error are automatically retried using an * exponential backoff strategy which adheres to AWS best practices. * * - **Unprocessed Keys**: Any `UnprocessedKeys` returned by the batch request are re-submitted. * * - **Exponential Backoff**: All retries are implemented with an exponential backoff strategy: * 1. First request: no delay * 2. Second request: delay `initialDelay` milliseconds (default: 100) * 3. All subsequent request delays are equal to the previous delay multiplied by the * `timeMultiplier` (default: 2), until either: * - The `maxRetries` limit is reached (default: 10), or * - The `maxDelay` limit is reached (default: 3500, or 3.5 seconds) * * Ergo, the base `delay` calculation can be summarized as follows: * > `initialDelay * timeMultiplier^attemptNumber milliseconds` * * If `useJitter` is true (default: false), the `delay` is randomized by applying the following * to the base `delay`: `Math.round( Math.random() * delay )`. Note that the determination as * to whether the delay exceeds the `maxDelay` is made BEFORE the jitter is applied. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html * * @param primaryKeys The primary keys of the items to get. * @param batchGetItemsOpts Options for the underlying `BatchGetItem` operation. * @returns The items, if found. * @throws {ItemInputError} If `primaryKeys` is not an array. */ readonly batchGetItems: (primaryKeys: Array<KeyParameters<Schema>>, batchGetItemsOpts?: BatchGetItemsOpts) => Promise<Array<ItemType> | undefined>; /** * A [`PutItem`][api-ref] operation wrapper which guarantees existing items will not be * overwritten by always including a [`ConditionExpression` which checks for the non-existence * of the item's hash key][ddb-docs-conditional-put]. * * If the Model's `schemaOptions` are configured to auto-add timestamps, this method will also add * a `createdAt` attribute (or the `attrName` specified for the custom timestamp attribute) set to * the current timestamp. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html * [ddb-docs-conditional-put]: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html#Expressions.ConditionExpressions.PreventingOverwrites * * @param item The item to create. * @param createItemOpts Options for the underlying `PutItem` operation. * @returns The provided `item` with any schema-defined defaults and transforms applied. */ readonly createItem: (item: ItemCreationParams, createItemOpts?: CreateItemOpts) => Promise<ItemType>; /** * A [`PutItem`][api-ref] operation wrapper which will either update an existing item or * create a new one if an item with the specified keys does not yet exist. * * > This method will overwrite an existing item with the specified keys if one exists. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html * * @param item The item to upsert. * @param upsertItemOpts Options for the underlying `PutItem` operation. * @returns The provided `item` with any schema-defined defaults and transforms applied. */ readonly upsertItem: (item: ItemCreationParams, upsertItemOpts?: UpsertItemOpts) => Promise<ItemType>; /** * A [`BatchWriteItem`][api-ref] operation wrapper optimized for upserting items. * * > Note: `BatchWriteItem` does not support condition expressions. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html * * @param items The items to upsert. * @param batchUpsertItemsOpts Options for the underlying `BatchWriteItem` operation. * @throws {ItemInputError} If `items` is not an array. * @returns The provided `items` with any schema-defined defaults and transformations applied. */ readonly batchUpsertItems: (items: Array<ItemCreationParams>, batchUpsertItemsOpts?: BatchWriteItemsOpts) => Promise<Array<ItemType>>; /** * [`UpdateItem`][api-ref] operation wrapper. This method uses the `update` param to generate the * following `UpdateItem` arguments: * * - `UpdateExpression` (may include `"SET"` and/or `"REMOVE"` clauses) * - `ExpressionAttributeNames` * - `ExpressionAttributeValues` * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html * * @param primaryKeys The primary keys of the item to update. * @param updateItemOpts The `update` object and options for the underlying `UpdateItem` operation. * @returns The updated item with new/updated values. */ readonly updateItem: (primaryKeys: KeyParameters<Schema>, { update, updateOptions, ...updateItemOpts }: UpdateItemOpts<PartialDeep<ItemCreationParams, { recurseIntoArrays: true; }>>) => Promise<ItemType>; /** * [`DeleteItem`][api-ref] operation wrapper. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_DeleteItem.html * * @param primaryKeys The primary keys of the item to delete. * @param deleteItemOpts Options for the underlying `DeleteItem` operation. * @returns The deleted item. */ readonly deleteItem: (primaryKeys: KeyParameters<Schema>, deleteItemOpts?: DeleteItemOpts) => Promise<ItemType>; /** * A [`BatchWriteItem`][api-ref] operation wrapper optimized for deleting items. * * > Note: `BatchWriteItem` does not support condition expressions. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html * * @param primaryKeys The primary keys of the items to delete. * @param batchDeleteItemsOpts Options for the underlying `BatchWriteItem` operation. */ readonly batchDeleteItems: (primaryKeys: Array<KeyParameters<Schema>>, batchDeleteItemsOpts?: BatchWriteItemsOpts) => Promise<Array<KeyParameters<Schema>>>; /** * A [`BatchWriteItem`][api-ref] operation wrapper which can be used for both * upserting and deleting items. Note that while each individual underlying Put/Delete operation * _is_ atomic, they're not atomic as a a whole, despite occurring within the same call (this is * an AWS implementation limitation). * * > Note: `BatchWriteItem` does not support condition expressions. * * - **Max Chunk Size**: The provided put-requests are broken into chunks of 25 (the max limit for * BatchWriteItem requests), and each chunk is submitted as a separate BatchWriteItem request. * * - **Automatic Retries**: Per AWS recommendations, batch requests which result in an error code * that indicates the provisioned throughput has been exceeded, or that the on-demand request * limit has been exceeded, are automatically retried. All other errors are re-thrown. * * - **Unprocessed Items**: Any `UnprocessedItems` returned by the batch request are re-submitted. * * - **Exponential Backoff**: All retries are implemented with an exponential backoff strategy: * 1. First request: no delay * 2. Second request: delay `initialDelay` milliseconds (default: 100) * 3. All subsequent request delays are equal to the previous delay multiplied by the * `timeMultiplier` (default: 2), until either: * - The `maxRetries` limit is reached (default: 10), or * - The `maxDelay` limit is reached (default: 3500, or 3.5 seconds) * * Ergo, the base `delay` calculation can be summarized as follows: * > `initialDelay * timeMultiplier^attemptNumber milliseconds` * * If `useJitter` is true (default: false), the `delay` is randomized by applying the following * to the base `delay`: `Math.round(Math.random() * delay)`. Note that the determination as to * whether the delay exceeds the `maxDelay` is made BEFORE the jitter is applied. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html * * @param upsertItems An array of items to upsert. * @param deleteItems An array of primary keys of items to delete. * @param batchUpsertItemsOpts Options for the underlying `BatchWriteItem` operation. * @throws {ItemInputError} If neither `upsertItems` nor `deleteItems` are arrays. */ readonly batchUpsertAndDeleteItems: ({ upsertItems, deleteItems, }: { upsertItems?: Array<ItemCreationParams>; deleteItems?: Array<KeyParameters<Schema>>; }, batchWriteItemsOpts?: BatchWriteItemsOpts) => Promise<{ upsertItems?: Array<ItemType>; deleteItems?: Array<KeyParameters<Schema>>; }>; /** * [`Query`][api-ref] operation wrapper which applies defaults and/or transforms defined in * this Model's schema to the returned items. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html * * // IDEA - Restrict QueryOpts.IndexName to only be a valid index name for the table. * * @param queryOpts Options for the underlying `Query` operation. * @returns The items, if found. */ readonly query: ({ where, limit, KeyConditionExpression, ExpressionAttributeNames, ExpressionAttributeValues, IndexName, ...queryOpts }: QueryOpts<ItemType>) => Promise<Array<ItemType>>; /** * [`Scan`][api-ref] operation wrapper which applies defaults and/or transforms defined in * this Model's schema to the returned items. * * [api-ref]: https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html * * // IDEA - Restrict ScanOpts.IndexName to only be a valid index name for the table. * * @param scanOpts Options for the underlying `Scan` operation. * @returns The items, if found. */ readonly scan: (scanOpts?: ScanOpts) => Promise<Array<ItemType>>; /** * Value-transforming action sets grouped by data-flow directionality. * * | `Method` | Description | * | :------- | :-----------------------------------------------------------------: | * | `toDB` | Actions executed on values _before_ being passed to the SDK client. | * | `fromDB` | Actions executed on values _returned_ from the SDK client. | */ readonly processItemAttributes: { [K in IODirection]: <ProcessedItemAttributes extends UnknownItem = BaseItem>(itemAttrs: UnknownItem, enabledIOActions?: EnabledIOActions<K>) => ProcessedItemAttributes; }; /** * This method applies an array of IO-Actions to the provided `itemAttrs` object. It sets default * IO-Actions context values which can be overridden via the {@link IOActionContext} parameter. * * @param itemAttrs The item attributes to apply IO-Actions to. * @param ioActionsSet The array of IO-Actions to apply. * @param ioActionsCtxOverrides Optional overrides for the IO-Actions context object. * @returns The item after being processed by the IO-Actions. */ private readonly applyIOActionsToItemAttributes; /** * This private Model method takes primary key args from public methods like `Model.getItem` * and applies key-specific IO-Actions accordingly. The IO-Actions context object provided to * the `applyIOActionsToItemAttributes` private method only contains the key attributes, i.e., * the provided `schema` only contains the `tableHashKey` and `tableRangeKey` attributes. */ private readonly processKeyArgs; } //# sourceMappingURL=Model.d.ts.map