@leanstacks/lambda-utils 0.2.0 → 0.3.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/dynamodb-client.d.ts +59 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/docs/DYNAMODB_CLIENT.md +214 -0
- package/docs/README.md +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DynamoDBClient, DynamoDBClientConfig } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { DynamoDBDocumentClient, marshallOptions as MarshallOptions, unmarshallOptions as UnmarshallOptions } from '@aws-sdk/lib-dynamodb';
|
|
3
|
+
/**
|
|
4
|
+
* Initializes both the DynamoDB client and DynamoDB Document client with the provided configuration.
|
|
5
|
+
* If the clients are already initialized, this will replace them with new instances.
|
|
6
|
+
*
|
|
7
|
+
* @param config - DynamoDB client configuration
|
|
8
|
+
* @param marshallOptions - Options for marshalling JavaScript objects to DynamoDB AttributeValues
|
|
9
|
+
* @param unmarshallOptions - Options for unmarshalling DynamoDB AttributeValues to JavaScript objects
|
|
10
|
+
* @returns An object containing both the base client and document client
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Initialize with default configuration
|
|
15
|
+
* initializeDynamoDBClients();
|
|
16
|
+
*
|
|
17
|
+
* // Initialize with custom configuration
|
|
18
|
+
* initializeDynamoDBClients(
|
|
19
|
+
* { region: 'us-east-1' },
|
|
20
|
+
* { removeUndefinedValues: true },
|
|
21
|
+
* { wrapNumbers: false }
|
|
22
|
+
* );
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare const initializeDynamoDBClients: (config?: DynamoDBClientConfig, marshallOptions?: MarshallOptions, unmarshallOptions?: UnmarshallOptions) => {
|
|
26
|
+
client: DynamoDBClient;
|
|
27
|
+
documentClient: DynamoDBDocumentClient;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Returns the singleton DynamoDB client instance.
|
|
31
|
+
* Throws an error if the client has not been initialized.
|
|
32
|
+
*
|
|
33
|
+
* @returns The DynamoDB client instance
|
|
34
|
+
* @throws Error if the client has not been initialized
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const client = getDynamoDBClient();
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare const getDynamoDBClient: () => DynamoDBClient;
|
|
42
|
+
/**
|
|
43
|
+
* Returns the singleton DynamoDB Document client instance.
|
|
44
|
+
* Throws an error if the client has not been initialized.
|
|
45
|
+
*
|
|
46
|
+
* @returns The DynamoDB Document client instance
|
|
47
|
+
* @throws Error if the client has not been initialized
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const docClient = getDynamoDBDocumentClient();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare const getDynamoDBDocumentClient: () => DynamoDBDocumentClient;
|
|
55
|
+
/**
|
|
56
|
+
* Resets both DynamoDB client instances.
|
|
57
|
+
* Useful for testing or when you need to reinitialize the clients with different configurations.
|
|
58
|
+
*/
|
|
59
|
+
export declare const resetDynamoDBClients: () => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { Logger, LoggerConfig, withRequestTracking } from './logging/logger';
|
|
2
2
|
export { createResponse, ok, created, noContent, badRequest, notFound, internalServerError, httpHeaders, Headers, } from './utils/apigateway-response';
|
|
3
|
+
export { getDynamoDBClient, getDynamoDBDocumentClient, initializeDynamoDBClients, resetDynamoDBClients, } from './clients/dynamodb-client';
|
|
4
|
+
export { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
5
|
+
export { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import n from"pino";import{lambdaRequestTracker as e,StructuredLogFormatter as t,CloudwatchLogFormatter as o,pinoLambdaDestination as i}from"pino-lambda";import{DynamoDBClient as l}from"@aws-sdk/client-dynamodb";export{DynamoDBClient}from"@aws-sdk/client-dynamodb";import{DynamoDBDocumentClient as r}from"@aws-sdk/lib-dynamodb";export{DynamoDBDocumentClient}from"@aws-sdk/lib-dynamodb";const s=e();class a{constructor(e){this._loggerConfig={enabled:!0,level:"info",format:"json"},this._instance=null,this._createLogger=()=>{const e="json"===this._loggerConfig.format?new t:new o,l=i({formatter:e});return n({enabled:this._loggerConfig.enabled,level:this._loggerConfig.level},l)},e&&(this._loggerConfig={enabled:e.enabled??!0,level:e.level??"info",format:e.format??"json"})}get instance(){return null===this._instance&&(this._instance=this._createLogger()),this._instance}}const m={contentType:n=>({"Content-Type":n}),json:{"Content-Type":"application/json"},cors:(n="*")=>({"Access-Control-Allow-Origin":n})},c=(n,e,t={})=>({statusCode:n,headers:{...t},body:JSON.stringify(e)}),d=(n,e={})=>c(200,n,e),g=(n,e={})=>c(201,n,e),f=(n={})=>c(204,{},n),u=(n="Bad Request",e={})=>c(400,{message:n},e),h=(n="Not Found",e={})=>c(404,{message:n},e),p=(n="Internal Server Error",e={})=>c(500,{message:n},e);let C=null,y=null;const D=(n,e,t)=>{C=new l(n||{});const o={marshallOptions:e||{},unmarshallOptions:t||{}};return y=r.from(C,o),{client:C,documentClient:y}},b=()=>{if(!C)throw new Error("DynamoDB client not initialized. Call initializeDynamoDBClients() first.");return C},w=()=>{if(!y)throw new Error("DynamoDB Document client not initialized. Call initializeDynamoDBClients() first.");return y},_=()=>{C=null,y=null};export{a as Logger,u as badRequest,c as createResponse,g as created,b as getDynamoDBClient,w as getDynamoDBDocumentClient,m as httpHeaders,D as initializeDynamoDBClients,p as internalServerError,f as noContent,h as notFound,d as ok,_ as resetDynamoDBClients,s as withRequestTracking};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("pino"),t=require("pino-lambda");const
|
|
1
|
+
"use strict";var e=require("pino"),t=require("pino-lambda"),n=require("@aws-sdk/client-dynamodb"),o=require("@aws-sdk/lib-dynamodb");const r=t.lambdaRequestTracker();const i=(e,t,n={})=>({statusCode:e,headers:{...n},body:JSON.stringify(t)});let s=null,a=null;Object.defineProperty(exports,"DynamoDBClient",{enumerable:!0,get:function(){return n.DynamoDBClient}}),Object.defineProperty(exports,"DynamoDBDocumentClient",{enumerable:!0,get:function(){return o.DynamoDBDocumentClient}}),exports.Logger=class{constructor(n){this._loggerConfig={enabled:!0,level:"info",format:"json"},this._instance=null,this._createLogger=()=>{const n="json"===this._loggerConfig.format?new t.StructuredLogFormatter:new t.CloudwatchLogFormatter,o=t.pinoLambdaDestination({formatter:n});return e({enabled:this._loggerConfig.enabled,level:this._loggerConfig.level},o)},n&&(this._loggerConfig={enabled:n.enabled??!0,level:n.level??"info",format:n.format??"json"})}get instance(){return null===this._instance&&(this._instance=this._createLogger()),this._instance}},exports.badRequest=(e="Bad Request",t={})=>i(400,{message:e},t),exports.createResponse=i,exports.created=(e,t={})=>i(201,e,t),exports.getDynamoDBClient=()=>{if(!s)throw new Error("DynamoDB client not initialized. Call initializeDynamoDBClients() first.");return s},exports.getDynamoDBDocumentClient=()=>{if(!a)throw new Error("DynamoDB Document client not initialized. Call initializeDynamoDBClients() first.");return a},exports.httpHeaders={contentType:e=>({"Content-Type":e}),json:{"Content-Type":"application/json"},cors:(e="*")=>({"Access-Control-Allow-Origin":e})},exports.initializeDynamoDBClients=(e,t,r)=>{s=new n.DynamoDBClient(e||{});const i={marshallOptions:t||{},unmarshallOptions:r||{}};return a=o.DynamoDBDocumentClient.from(s,i),{client:s,documentClient:a}},exports.internalServerError=(e="Internal Server Error",t={})=>i(500,{message:e},t),exports.noContent=(e={})=>i(204,{},e),exports.notFound=(e="Not Found",t={})=>i(404,{message:e},t),exports.ok=(e,t={})=>i(200,e,t),exports.resetDynamoDBClients=()=>{s=null,a=null},exports.withRequestTracking=r;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# DynamoDB Client Utilities
|
|
2
|
+
|
|
3
|
+
The DynamoDB client utilities provide reusable singleton instances of `DynamoDBClient` and `DynamoDBDocumentClient` for use in AWS Lambda functions. These utilities enable you to configure clients once and reuse them across invocations, following AWS best practices for Lambda performance optimization.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The utility exports the following functions:
|
|
8
|
+
|
|
9
|
+
- `initializeDynamoDBClients()` - Initialize both DynamoDB clients with optional configuration
|
|
10
|
+
- `getDynamoDBClient()` - Get the singleton DynamoDB client instance
|
|
11
|
+
- `getDynamoDBDocumentClient()` - Get the singleton DynamoDB Document client instance
|
|
12
|
+
- `resetDynamoDBClients()` - Reset both client instances (useful for testing)
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Basic Usage
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { initializeDynamoDBClients, getDynamoDBDocumentClient } from '@leanstacks/lambda-utils';
|
|
20
|
+
import { PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
21
|
+
|
|
22
|
+
// Initialize both clients (typically done once outside the handler)
|
|
23
|
+
initializeDynamoDBClients({ region: 'us-east-1' });
|
|
24
|
+
|
|
25
|
+
export const handler = async (event: any) => {
|
|
26
|
+
// Get the document client (most common use case)
|
|
27
|
+
const docClient = getDynamoDBDocumentClient();
|
|
28
|
+
|
|
29
|
+
// Use the document client
|
|
30
|
+
await docClient.send(
|
|
31
|
+
new PutCommand({
|
|
32
|
+
TableName: 'MyTable',
|
|
33
|
+
Item: { id: '123', name: 'Example' },
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Using the Base DynamoDB Client
|
|
40
|
+
|
|
41
|
+
````typescript
|
|
42
|
+
import { initializeDynamoDBClients, getDynamoDBClient } from '@leanstacks/lambda-utils';
|
|
43
|
+
import { PutItemCommand } from '@aws-sdk/client-dynamodb';
|
|
44
|
+
|
|
45
|
+
// Initialize both clients
|
|
46
|
+
initializeDynamoDBClients({ region: 'us-east-1' });
|
|
47
|
+
|
|
48
|
+
export const handler = async (event: any) => {
|
|
49
|
+
// Get the base client for advanced use cases
|
|
50
|
+
const client = getDynamoDBClient();
|
|
51
|
+
|
|
52
|
+
// Use the base client
|
|
53
|
+
await client.send(new PutItemCommand({
|
|
54
|
+
TableName: 'MyTable',
|
|
55
|
+
Item: { id: { S: '123' }, name: { S: 'Example' } }
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
### Advanced Configuration
|
|
59
|
+
|
|
60
|
+
#### Custom DynamoDB Client Configuration
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { initializeDynamoDBClients } from '@leanstacks/lambda-utils';
|
|
64
|
+
|
|
65
|
+
initializeDynamoDBClients({
|
|
66
|
+
region: 'us-west-2',
|
|
67
|
+
endpoint: 'http://localhost:8000', // For local development
|
|
68
|
+
credentials: {
|
|
69
|
+
accessKeyId: 'local',
|
|
70
|
+
secretAccessKey: 'local',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
````
|
|
74
|
+
|
|
75
|
+
#### Custom Marshall/Unmarshall Options
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { initializeDynamoDBClients } from '@leanstacks/lambda-utils';
|
|
79
|
+
|
|
80
|
+
initializeDynamoDBClients(
|
|
81
|
+
{ region: 'us-east-1' },
|
|
82
|
+
{
|
|
83
|
+
// Marshall options
|
|
84
|
+
removeUndefinedValues: true,
|
|
85
|
+
convertEmptyValues: false,
|
|
86
|
+
convertClassInstanceToMap: true,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
// Unmarshall options
|
|
90
|
+
wrapNumbers: false,
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Lambda Handler Pattern
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { initializeDynamoDBClients, getDynamoDBDocumentClient } from '@leanstacks/lambda-utils';
|
|
99
|
+
import { GetCommand } from '@aws-sdk/lib-dynamodb';
|
|
100
|
+
|
|
101
|
+
// Initialize clients outside the handler (runs once per cold start)
|
|
102
|
+
initializeDynamoDBClients({ region: process.env.AWS_REGION }, { removeUndefinedValues: true });
|
|
103
|
+
|
|
104
|
+
// Handler function
|
|
105
|
+
export const handler = async (event: any) => {
|
|
106
|
+
const docClient = getDynamoDBDocumentClient();
|
|
107
|
+
|
|
108
|
+
const result = await docClient.send(
|
|
109
|
+
new GetCommand({
|
|
110
|
+
TableName: process.env.TABLE_NAME,
|
|
111
|
+
Key: { id: event.id },
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return result.Item;
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
### `initializeDynamoDBClients(config?, marshallOptions?, unmarshallOptions?): { client: DynamoDBClient; documentClient: DynamoDBDocumentClient }`
|
|
122
|
+
|
|
123
|
+
Initializes both the DynamoDB client and DynamoDB Document client with the provided configuration.
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
|
|
127
|
+
- `config` (optional) - DynamoDB client configuration object
|
|
128
|
+
- `marshallOptions` (optional) - Options for marshalling JavaScript objects to DynamoDB AttributeValues
|
|
129
|
+
- `unmarshallOptions` (optional) - Options for unmarshalling DynamoDB AttributeValues to JavaScript objects
|
|
130
|
+
|
|
131
|
+
**Returns:**
|
|
132
|
+
|
|
133
|
+
- An object containing both `client` (DynamoDBClient) and `documentClient` (DynamoDBDocumentClient)
|
|
134
|
+
|
|
135
|
+
**Notes:**
|
|
136
|
+
|
|
137
|
+
- Creates both clients in a single call
|
|
138
|
+
- If called multiple times, it will replace the existing client instances
|
|
139
|
+
- If no config is provided, uses default AWS SDK configuration
|
|
140
|
+
- Most users will only need the `documentClient` from the return value or via `getDynamoDBDocumentClient()`
|
|
141
|
+
|
|
142
|
+
### `getDynamoDBClient(): DynamoDBClient`
|
|
143
|
+
|
|
144
|
+
Returns the singleton DynamoDB client instance.
|
|
145
|
+
|
|
146
|
+
**Returns:**
|
|
147
|
+
|
|
148
|
+
- The `DynamoDBClient` instance
|
|
149
|
+
|
|
150
|
+
**Throws:**
|
|
151
|
+
|
|
152
|
+
- Error if the client has not been initialized
|
|
153
|
+
|
|
154
|
+
### `getDynamoDBDocumentClient(): DynamoDBDocumentClient`
|
|
155
|
+
|
|
156
|
+
Returns the singleton DynamoDB Document client instance.
|
|
157
|
+
|
|
158
|
+
**Returns:**
|
|
159
|
+
|
|
160
|
+
- The `DynamoDBDocumentClient` instance
|
|
161
|
+
|
|
162
|
+
**Throws:**
|
|
163
|
+
|
|
164
|
+
- Error if the document client has not been initialized
|
|
165
|
+
|
|
166
|
+
### `resetDynamoDBClients(): void`
|
|
167
|
+
|
|
168
|
+
Resets both DynamoDB client instances to null.
|
|
169
|
+
|
|
170
|
+
**Notes:**
|
|
171
|
+
|
|
172
|
+
- Primarily useful for testing scenarios where you need to reinitialize clients with different configurations
|
|
173
|
+
- After calling this, you must reinitialize the clients before using the getter functions
|
|
174
|
+
|
|
175
|
+
## Best Practices
|
|
176
|
+
|
|
177
|
+
1. **Initialize Outside the Handler**: Always initialize clients outside your Lambda handler function to reuse the instance across invocations.
|
|
178
|
+
|
|
179
|
+
2. **Use Environment Variables**: Configure clients using environment variables for flexibility across environments.
|
|
180
|
+
|
|
181
|
+
3. **Error Handling**: Always wrap client operations in try-catch blocks to handle errors gracefully.
|
|
182
|
+
|
|
183
|
+
4. **Testing**: Use `resetDynamoDBClients()` in test setup/teardown to ensure clean test isolation.
|
|
184
|
+
|
|
185
|
+
## Testing Example
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { initializeDynamoDBClients, getDynamoDBDocumentClient, resetDynamoDBClients } from '@leanstacks/lambda-utils';
|
|
189
|
+
|
|
190
|
+
describe('MyLambdaHandler', () => {
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
resetDynamoDBClients();
|
|
193
|
+
initializeDynamoDBClients(
|
|
194
|
+
{ region: 'us-east-1', endpoint: 'http://localhost:8000' },
|
|
195
|
+
{ removeUndefinedValues: true },
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
afterEach(() => {
|
|
200
|
+
resetDynamoDBClients();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should retrieve item from DynamoDB', async () => {
|
|
204
|
+
// Your test code here
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Related Resources
|
|
210
|
+
|
|
211
|
+
- **[AWS SDK for JavaScript v3 - DynamoDB Client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-dynamodb/)**
|
|
212
|
+
- **[AWS SDK for JavaScript v3 - DynamoDB Document Client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/Class/DynamoDBDocumentClient/)**
|
|
213
|
+
- **[AWS Lambda Best Practices](https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html)**
|
|
214
|
+
- **[Back to the project documentation](README.md)**
|
package/docs/README.md
CHANGED
|
@@ -10,8 +10,8 @@ Lambda Utilities is a collection of pre-configured tools and helpers designed to
|
|
|
10
10
|
|
|
11
11
|
- **[Logging Guide](./LOGGING.md)** – Implement structured logging in your Lambda functions with Pino and automatic AWS context enrichment
|
|
12
12
|
- **[API Gateway Responses](./API_GATEWAY_RESPONSES.md)** – Format Lambda responses for API Gateway with standard HTTP status codes and headers
|
|
13
|
+
- **[DynamoDB Client](./DYNAMODB_CLIENT.md)** – Reusable singleton DynamoDB client instances with custom configuration
|
|
13
14
|
- **[Configuration](./CONFIGURATION.md)** – Validate environment variables and configuration with Zod type safety
|
|
14
|
-
- **[AWS Clients](./CLIENTS.md)** – Pre-configured AWS SDK v3 clients optimized for Lambda
|
|
15
15
|
- **[Getting Started](./GETTING_STARTED.md)** – Quick setup and installation instructions
|
|
16
16
|
|
|
17
17
|
## Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leanstacks/lambda-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-alpha.2",
|
|
4
4
|
"description": "A collection of utilities and helper functions designed to streamline the development of AWS Lambda functions using TypeScript.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -63,7 +63,9 @@
|
|
|
63
63
|
"typescript": "5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"
|
|
67
|
-
"
|
|
66
|
+
"@aws-sdk/client-dynamodb": "^3.955.0",
|
|
67
|
+
"@aws-sdk/lib-dynamodb": "^3.955.0",
|
|
68
|
+
"pino": "^10.1.0",
|
|
69
|
+
"pino-lambda": "^4.4.1"
|
|
68
70
|
}
|
|
69
71
|
}
|