npm package: @qezor/dynakit
Description: Modular DynamoDB toolkit with precise exports, expression builders, and ergonomic document-client helpers
Author: Abdul Ahad
Latest version: 1.0.3

# @qezor/dynakit

Modular DynamoDB helpers with precise exports, expression builders, and document-client utilities.

`dynakit` is built for the stuff we keep writing over and over in Lambda services:
- create DynamoDB clients and document clients
- build update, projection, and condition expressions
- run get/put/update/delete/query/scan operations
- handle batch and transaction patterns
- paginate cleanly
- keep imports precise when the package grows

## Installation

```bash
npm install @qezor/dynakit
```

## Precise Imports

Use only what you need:

```js
const { createDocClient } = require("@qezor/dynakit/client")
const { getItem, updateItem } = require("@qezor/dynakit/crud")
const { eq, and, buildConditionExpression } = require("@qezor/dynakit/conditions")
const { buildUpdateExpression, ifNotExists, attribute } = require("@qezor/dynakit/expressions")
```

Or use the full entry:

```js
const { createDocClient, getItem, queryAll } = require("@qezor/dynakit")
```

## Package Surface

- `@qezor/dynakit/client`
- `@qezor/dynakit/errors`
- `@qezor/dynakit/helpers`
- `@qezor/dynakit/expressions`
- `@qezor/dynakit/conditions`
- `@qezor/dynakit/crud`
- `@qezor/dynakit/query`
- `@qezor/dynakit/batch`
- `@qezor/dynakit/transact`
- `@qezor/dynakit/paging`

## Quick Start

```js
const { createDocClient } = require("@qezor/dynakit/client")
const { getItem, updateItem } = require("@qezor/dynakit/crud")
const { eq } = require("@qezor/dynakit/conditions")

const ddb = createDocClient()

const user = await getItem(ddb, {
  tableName: "qezor-users",
  key: { userId: "u_123" },
})

await updateItem(ddb, {
  tableName: "qezor-users",
  key: { userId: "u_123" },
  set: {
    lastLoginAt: Date.now(),
  },
  condition: eq("userId", "u_123"),
  returnValues: "ALL_NEW",
})
```

## Expression Builders

### Projection

```js
const { buildProjectionExpression } = require("@qezor/dynakit/expressions")

buildProjectionExpression(["userId", "profile.displayName"])
```

### Update

```js
const { buildUpdateExpression, ifNotExists, listAppend, attribute } = require("@qezor/dynakit/expressions")

buildUpdateExpression({
  set: {
    updatedAt: Date.now(),
    loginCount: ifNotExists("loginCount", 0),
    tags: listAppend(attribute("tags"), ["new"]),
  },
  remove: ["legacyField"],
})
```

### Conditions

```js
const { and, eq, notExists, beginsWith } = require("@qezor/dynakit/conditions")

const condition = and(
  eq("tenantId", "tenant_1"),
  notExists("deletedAt"),
  beginsWith("usernameNormalized", "ab")
)
```

## Query + Scan

```js
const { queryItems, queryAll, scanItems } = require("@qezor/dynakit/query")
const { eq, beginsWith } = require("@qezor/dynakit/conditions")

const firstPage = await queryItems(ddb, {
  tableName: "qezor-users",
  indexName: "emailNormalized-index",
  keyCondition: eq("emailNormalized", "a@qezor.pro"),
})

const all = await queryAll(ddb, {
  tableName: "qezor-users",
  indexName: "usernameNormalized-index",
  keyCondition: beginsWith("usernameNormalized", "ab"),
})
```

## Batch + Transaction Helpers

### Batch Get for One Table

```js
const { batchGetTable } = require("@qezor/dynakit/batch")

const users = await batchGetTable(ddb, {
  tableName: "qezor-users",
  keys: [{ userId: "u1" }, { userId: "u2" }],
})
```

### Transaction Write

```js
const { transactWriteItems } = require("@qezor/dynakit/transact")

await transactWriteItems(ddb, {
  items: [
    {
      Put: {
        TableName: "qezor-sessions",
        Item: { sessionId: "s1", userId: "u1" },
      },
    },
    {
      Delete: {
        TableName: "qezor-sessions",
        Key: { sessionId: "s0" },
      },
    },
  ],
})
```

## Pagination

```js
const { paginateQuery } = require("@qezor/dynakit/paging")

for await (const page of paginateQuery(ddb, {
  tableName: "qezor-users",
  indexName: "usernameNormalized-index",
  keyCondition: beginsWith("usernameNormalized", "ab"),
})) {
  console.log(page.items)
}
```

## Helper Utilities

- `nowEpoch()`
- `ttlFromNow(seconds, now?)`
- `chunk(list, size)`
- `compactUndefined(object)`
- `withTimestamps(item, options?)`
- `touchUpdatedAt(item, options?)`

## Notes

- AWS SDK modules are loaded lazily, so pure helpers can still be used and tested without touching DynamoDB.
- `dynakit` is built around `DynamoDBDocumentClient` style usage.
- Condition builders are generic and do not enforce DynamoDB key-condition restrictions for you. That stays the caller's responsibility.
- This package stays focused on reusable DynamoDB mechanics, not domain rules.