@leanstacks/lambda-utils 0.3.0-alpha.4 → 0.4.0-alpha.1
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/README.md +135 -4
- package/dist/clients/lambda-client.d.ts +78 -0
- package/dist/clients/sns-client.d.ts +75 -0
- package/dist/clients/sqs-client.d.ts +74 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/docs/CONFIGURATION.md +348 -0
- package/docs/LAMBDA_CLIENT.md +292 -0
- package/docs/README.md +5 -3
- package/docs/SNS_CLIENT.md +363 -0
- package/docs/SQS_CLIENT.md +352 -0
- package/package.json +10 -6
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Configuration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to use the `createConfigManager` utility to validate and manage environment variables in your Lambda functions with full TypeScript type safety.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The configuration utility provides:
|
|
8
|
+
|
|
9
|
+
- **Schema-based validation** using Zod for environment variables
|
|
10
|
+
- **Type-safe access** to your configuration with full TypeScript support
|
|
11
|
+
- **Caching** of validated configuration for performance
|
|
12
|
+
- **Flexible defaults** for optional environment variables
|
|
13
|
+
- **Clear error messages** when validation fails
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
### Define Your Schema
|
|
18
|
+
|
|
19
|
+
Create a Zod schema that describes your environment variables:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
|
|
24
|
+
const configSchema = z.object({
|
|
25
|
+
// Required variables
|
|
26
|
+
TABLE_NAME: z.string().min(1, 'TABLE_NAME is required'),
|
|
27
|
+
|
|
28
|
+
// Optional with defaults
|
|
29
|
+
AWS_REGION: z.string().default('us-east-1'),
|
|
30
|
+
DEBUG_MODE: z
|
|
31
|
+
.enum(['true', 'false'])
|
|
32
|
+
.default('false')
|
|
33
|
+
.transform((val) => val === 'true'),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Infer the TypeScript type from your schema
|
|
37
|
+
type Config = z.infer<typeof configSchema>;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Create and Use ConfigManager
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { createConfigManager } from '@leanstacks/lambda-utils';
|
|
44
|
+
|
|
45
|
+
// Create the manager
|
|
46
|
+
const configManager = createConfigManager(configSchema);
|
|
47
|
+
|
|
48
|
+
// Get validated config (cached after first call)
|
|
49
|
+
const config = configManager.get();
|
|
50
|
+
|
|
51
|
+
// Use your configuration
|
|
52
|
+
console.log(config.TABLE_NAME); // Type-safe access
|
|
53
|
+
console.log(config.AWS_REGION); // Automatically defaults to 'us-east-1'
|
|
54
|
+
console.log(config.DEBUG_MODE); // Typed as boolean
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Complete Example
|
|
58
|
+
|
|
59
|
+
Here's a realistic Lambda function configuration:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { z } from 'zod';
|
|
63
|
+
import { createConfigManager } from '@leanstacks/lambda-utils';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Schema for validating environment variables
|
|
67
|
+
*/
|
|
68
|
+
const envSchema = z.object({
|
|
69
|
+
// Required variables
|
|
70
|
+
TASKS_TABLE: z.string().min(1, 'TASKS_TABLE environment variable is required'),
|
|
71
|
+
|
|
72
|
+
// Optional variables with defaults
|
|
73
|
+
AWS_REGION: z.string().default('us-east-1'),
|
|
74
|
+
|
|
75
|
+
// Logging configuration
|
|
76
|
+
LOGGING_ENABLED: z
|
|
77
|
+
.enum(['true', 'false'] as const)
|
|
78
|
+
.default('true')
|
|
79
|
+
.transform((val) => val === 'true'),
|
|
80
|
+
LOGGING_LEVEL: z.enum(['debug', 'info', 'warn', 'error'] as const).default('debug'),
|
|
81
|
+
LOGGING_FORMAT: z.enum(['text', 'json'] as const).default('json'),
|
|
82
|
+
|
|
83
|
+
// CORS configuration
|
|
84
|
+
CORS_ALLOW_ORIGIN: z.string().default('*'),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Type representing our validated config
|
|
89
|
+
*/
|
|
90
|
+
export type Config = z.infer<typeof envSchema>;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Configuration manager instance
|
|
94
|
+
*/
|
|
95
|
+
const configManager = createConfigManager(envSchema);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validated configuration object. Singleton.
|
|
99
|
+
*/
|
|
100
|
+
export const config = configManager.get();
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Refresh configuration (useful in tests)
|
|
104
|
+
*/
|
|
105
|
+
export const refreshConfig = () => configManager.refresh();
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Then use it in your handler:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { config } from './config';
|
|
112
|
+
import { Logger } from '@leanstacks/lambda-utils';
|
|
113
|
+
|
|
114
|
+
const logger = new Logger({
|
|
115
|
+
level: config.LOGGING_LEVEL,
|
|
116
|
+
format: config.LOGGING_FORMAT,
|
|
117
|
+
}).instance;
|
|
118
|
+
|
|
119
|
+
export const handler = async (event: any) => {
|
|
120
|
+
logger.info({
|
|
121
|
+
message: 'Processing request',
|
|
122
|
+
table: config.TASKS_TABLE,
|
|
123
|
+
region: config.AWS_REGION,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Your handler logic here
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API Reference
|
|
131
|
+
|
|
132
|
+
### `createConfigManager<T>(schema: T): ConfigManager<z.infer<T>>`
|
|
133
|
+
|
|
134
|
+
Creates a configuration manager instance.
|
|
135
|
+
|
|
136
|
+
**Parameters:**
|
|
137
|
+
|
|
138
|
+
- `schema` - A Zod schema defining your environment variables
|
|
139
|
+
|
|
140
|
+
**Returns:** A `ConfigManager` instance with two methods
|
|
141
|
+
|
|
142
|
+
### `ConfigManager.get(): T`
|
|
143
|
+
|
|
144
|
+
Gets the validated configuration (cached after the first call).
|
|
145
|
+
|
|
146
|
+
**Throws:** `Error` if validation fails
|
|
147
|
+
|
|
148
|
+
**Returns:** The validated configuration object
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const config = configManager.get();
|
|
152
|
+
// First call: validates and caches
|
|
153
|
+
// Subsequent calls: returns cached value
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### `ConfigManager.refresh(): T`
|
|
157
|
+
|
|
158
|
+
Refreshes the configuration by re-validating environment variables against the schema.
|
|
159
|
+
|
|
160
|
+
**Throws:** `Error` if validation fails
|
|
161
|
+
|
|
162
|
+
**Returns:** The newly validated configuration object
|
|
163
|
+
|
|
164
|
+
Use this in tests when you need to change environment variables:
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
beforeEach(() => {
|
|
168
|
+
process.env.TABLE_NAME = 'test-table';
|
|
169
|
+
configManager.refresh(); // Re-validate with new values
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Best Practices
|
|
174
|
+
|
|
175
|
+
### 1. Separate Configuration Module
|
|
176
|
+
|
|
177
|
+
Create a dedicated configuration module for your Lambda function:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// src/config.ts
|
|
181
|
+
import { z } from 'zod';
|
|
182
|
+
import { createConfigManager } from '@leanstacks/lambda-utils';
|
|
183
|
+
|
|
184
|
+
const schema = z.object({
|
|
185
|
+
TABLE_NAME: z.string().min(1),
|
|
186
|
+
AWS_REGION: z.string().default('us-east-1'),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export type Config = z.infer<typeof schema>;
|
|
190
|
+
|
|
191
|
+
const configManager = createConfigManager(schema);
|
|
192
|
+
|
|
193
|
+
export const config = configManager.get();
|
|
194
|
+
export const refresh = () => configManager.refresh();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 2. Validate Early
|
|
198
|
+
|
|
199
|
+
Call `config.get()` during handler initialization to validate configuration before processing requests:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
export const handler = async (event: any, context: any) => {
|
|
203
|
+
// Validation happens here, fails fast if config is invalid
|
|
204
|
+
const config = configManager.get();
|
|
205
|
+
|
|
206
|
+
// Handler logic with validated config
|
|
207
|
+
};
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 3. Use Enums for Known Values
|
|
211
|
+
|
|
212
|
+
Use `z.enum()` for configuration options with limited valid values:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const schema = z.object({
|
|
216
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
217
|
+
ENVIRONMENT: z.enum(['dev', 'staging', 'prod']),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// TypeScript autocomplete for config.LOG_LEVEL
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 4. Transform String to Boolean
|
|
224
|
+
|
|
225
|
+
Since environment variables are always strings, use `transform()` to convert them:
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const schema = z.object({
|
|
229
|
+
ENABLE_FEATURE: z
|
|
230
|
+
.enum(['true', 'false'])
|
|
231
|
+
.default('false')
|
|
232
|
+
.transform((val) => val === 'true'),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// config.ENABLE_FEATURE is now a boolean
|
|
236
|
+
if (config.ENABLE_FEATURE) {
|
|
237
|
+
// Feature is enabled
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 5. Provide Helpful Error Messages
|
|
242
|
+
|
|
243
|
+
Use Zod's second parameter to provide context-specific error messages:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const schema = z.object({
|
|
247
|
+
DATABASE_URL: z.string().url('DATABASE_URL must be a valid URL'),
|
|
248
|
+
API_KEY: z.string().min(32, 'API_KEY must be at least 32 characters'),
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### 6. Test Configuration Validation
|
|
253
|
+
|
|
254
|
+
Test that your schema properly validates configuration:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { config, refresh } from './config';
|
|
258
|
+
|
|
259
|
+
describe('Configuration', () => {
|
|
260
|
+
it('should load default values', () => {
|
|
261
|
+
delete process.env.AWS_REGION;
|
|
262
|
+
refresh();
|
|
263
|
+
expect(config.AWS_REGION).toBe('us-east-1');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should validate required variables', () => {
|
|
267
|
+
delete process.env.TABLE_NAME;
|
|
268
|
+
expect(() => refresh()).toThrow();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should parse boolean values', () => {
|
|
272
|
+
process.env.DEBUG_MODE = 'true';
|
|
273
|
+
refresh();
|
|
274
|
+
expect(config.DEBUG_MODE).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Common Patterns
|
|
280
|
+
|
|
281
|
+
### Database Configuration
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const schema = z.object({
|
|
285
|
+
DATABASE_URL: z.string().url(),
|
|
286
|
+
DATABASE_POOL_SIZE: z
|
|
287
|
+
.string()
|
|
288
|
+
.default('10')
|
|
289
|
+
.transform((val) => parseInt(val, 10)),
|
|
290
|
+
DATABASE_TIMEOUT: z
|
|
291
|
+
.string()
|
|
292
|
+
.default('5000')
|
|
293
|
+
.transform((val) => parseInt(val, 10)),
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Feature Flags
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
const schema = z.object({
|
|
301
|
+
FEATURE_NEW_UI: z
|
|
302
|
+
.enum(['true', 'false'])
|
|
303
|
+
.default('false')
|
|
304
|
+
.transform((val) => val === 'true'),
|
|
305
|
+
FEATURE_BETA_API: z
|
|
306
|
+
.enum(['true', 'false'])
|
|
307
|
+
.default('false')
|
|
308
|
+
.transform((val) => val === 'true'),
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Multi-Environment Setup
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const schema = z.object({
|
|
316
|
+
ENVIRONMENT: z.enum(['development', 'staging', 'production']),
|
|
317
|
+
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
318
|
+
DEBUG_MODE: z
|
|
319
|
+
.enum(['true', 'false'])
|
|
320
|
+
.refine(
|
|
321
|
+
(val) => (val === 'true' ? process.env.ENVIRONMENT === 'development' : true),
|
|
322
|
+
'DEBUG_MODE can only be true in development',
|
|
323
|
+
)
|
|
324
|
+
.transform((val) => val === 'true'),
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Error Handling
|
|
329
|
+
|
|
330
|
+
Configuration validation errors include detailed information about what failed:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
try {
|
|
334
|
+
const config = configManager.get();
|
|
335
|
+
} catch (error) {
|
|
336
|
+
if (error instanceof Error) {
|
|
337
|
+
console.error(error.message);
|
|
338
|
+
// Output: "Configuration validation failed: TABLE_NAME: String must contain at least 1 character"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Lambda will automatically fail fast if configuration is invalid, which is the desired behavior for Lambda functions.
|
|
344
|
+
|
|
345
|
+
## Related Documentation
|
|
346
|
+
|
|
347
|
+
- **[Zod Documentation](https://zod.dev/)** – Learn more about schema validation with Zod
|
|
348
|
+
- **[Back to the project documentation](README.md)**
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Lambda Client Utilities
|
|
2
|
+
|
|
3
|
+
The Lambda client utilities provide a reusable singleton instance of `LambdaClient` for use in AWS Lambda functions. These utilities enable you to configure the client once and reuse it across invocations, following AWS best practices for Lambda performance optimization.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The utility exports the following functions:
|
|
8
|
+
|
|
9
|
+
- `initializeLambdaClient()` - Initialize the Lambda client with optional configuration
|
|
10
|
+
- `getLambdaClient()` - Get the singleton Lambda client instance
|
|
11
|
+
- `invokeLambdaSync()` - Invoke a Lambda function synchronously (RequestResponse)
|
|
12
|
+
- `invokeLambdaAsync()` - Invoke a Lambda function asynchronously (Event)
|
|
13
|
+
- `resetLambdaClient()` - Reset the client instance (useful for testing)
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Synchronous Invocation (RequestResponse)
|
|
18
|
+
|
|
19
|
+
Use synchronous invocation when you need to wait for the Lambda function to complete and return a response:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { invokeLambdaSync } from '@leanstacks/lambda-utils';
|
|
23
|
+
|
|
24
|
+
export const handler = async (event: any) => {
|
|
25
|
+
// Invoke a Lambda function and wait for the response
|
|
26
|
+
const response = await invokeLambdaSync('my-function-name', {
|
|
27
|
+
key: 'value',
|
|
28
|
+
data: { nested: true },
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
statusCode: 200,
|
|
33
|
+
body: JSON.stringify(response),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Synchronous Invocation with Typed Response
|
|
39
|
+
|
|
40
|
+
For better type safety, you can specify the expected response type:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { invokeLambdaSync } from '@leanstacks/lambda-utils';
|
|
44
|
+
|
|
45
|
+
interface MyFunctionResponse {
|
|
46
|
+
result: string;
|
|
47
|
+
statusCode: number;
|
|
48
|
+
data: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const handler = async (event: any) => {
|
|
52
|
+
const response = await invokeLambdaSync<MyFunctionResponse>('my-function-name', {
|
|
53
|
+
operation: 'getData',
|
|
54
|
+
id: '12345',
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
console.log(`Function returned: ${response.result}`);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
statusCode: response.statusCode,
|
|
61
|
+
body: JSON.stringify(response.data),
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Asynchronous Invocation (Event)
|
|
67
|
+
|
|
68
|
+
Use asynchronous invocation for fire-and-forget scenarios where you don't need to wait for the Lambda function to complete:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { invokeLambdaAsync } from '@leanstacks/lambda-utils';
|
|
72
|
+
|
|
73
|
+
export const handler = async (event: any) => {
|
|
74
|
+
// Invoke a Lambda function asynchronously (fire and forget)
|
|
75
|
+
await invokeLambdaAsync('my-async-function', {
|
|
76
|
+
eventType: 'process',
|
|
77
|
+
data: [1, 2, 3],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Returns immediately without waiting for the function to complete
|
|
81
|
+
return {
|
|
82
|
+
statusCode: 202,
|
|
83
|
+
body: JSON.stringify({ message: 'Processing initiated' }),
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Using Function ARN
|
|
89
|
+
|
|
90
|
+
You can invoke Lambda functions by name or ARN:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { invokeLambdaSync } from '@leanstacks/lambda-utils';
|
|
94
|
+
|
|
95
|
+
export const handler = async (event: any) => {
|
|
96
|
+
// Using function ARN
|
|
97
|
+
const response = await invokeLambdaSync('arn:aws:lambda:us-east-1:123456789012:function:my-function', {
|
|
98
|
+
key: 'value',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
statusCode: 200,
|
|
103
|
+
body: JSON.stringify(response),
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Using the Lambda Client Directly
|
|
109
|
+
|
|
110
|
+
For advanced use cases, you can access the underlying Lambda client:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { getLambdaClient } from '@leanstacks/lambda-utils';
|
|
114
|
+
import { ListFunctionsCommand } from '@aws-sdk/client-lambda';
|
|
115
|
+
|
|
116
|
+
export const handler = async (event: any) => {
|
|
117
|
+
const client = getLambdaClient();
|
|
118
|
+
|
|
119
|
+
const response = await client.send(new ListFunctionsCommand({}));
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
statusCode: 200,
|
|
123
|
+
body: JSON.stringify(response.Functions),
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Custom Configuration
|
|
129
|
+
|
|
130
|
+
Initialize the Lambda client with custom configuration at the start of your Lambda handler:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { initializeLambdaClient, invokeLambdaSync } from '@leanstacks/lambda-utils';
|
|
134
|
+
|
|
135
|
+
export const handler = async (event: any) => {
|
|
136
|
+
// Initialize with custom configuration (only needs to be done once)
|
|
137
|
+
initializeLambdaClient({
|
|
138
|
+
region: 'us-west-2',
|
|
139
|
+
maxAttempts: 3,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const response = await invokeLambdaSync('my-function', { key: 'value' });
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
statusCode: 200,
|
|
146
|
+
body: JSON.stringify(response),
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## API Reference
|
|
152
|
+
|
|
153
|
+
### initializeLambdaClient(config?)
|
|
154
|
+
|
|
155
|
+
Initializes the Lambda client with the provided configuration. If the client is already initialized, this will replace it with a new instance.
|
|
156
|
+
|
|
157
|
+
**Parameters:**
|
|
158
|
+
|
|
159
|
+
- `config` (optional): `LambdaClientConfig` - AWS SDK Lambda client configuration
|
|
160
|
+
|
|
161
|
+
**Returns:** `LambdaClient` - The Lambda client instance
|
|
162
|
+
|
|
163
|
+
### getLambdaClient()
|
|
164
|
+
|
|
165
|
+
Returns the singleton Lambda client instance. If the client has not been initialized, creates one with default configuration.
|
|
166
|
+
|
|
167
|
+
**Returns:** `LambdaClient` - The Lambda client instance
|
|
168
|
+
|
|
169
|
+
### invokeLambdaSync<T>(functionName, payload)
|
|
170
|
+
|
|
171
|
+
Invokes a Lambda function synchronously (RequestResponse). The function waits for the response and returns the payload.
|
|
172
|
+
|
|
173
|
+
**Parameters:**
|
|
174
|
+
|
|
175
|
+
- `functionName`: `string` - The name or ARN of the Lambda function to invoke
|
|
176
|
+
- `payload`: `unknown` - The JSON payload to pass to the Lambda function
|
|
177
|
+
|
|
178
|
+
**Returns:** `Promise<T>` - Promise that resolves to the response payload from the Lambda function
|
|
179
|
+
|
|
180
|
+
**Throws:** `Error` - If the Lambda invocation fails or returns a function error
|
|
181
|
+
|
|
182
|
+
### invokeLambdaAsync(functionName, payload)
|
|
183
|
+
|
|
184
|
+
Invokes a Lambda function asynchronously (Event). The function returns immediately without waiting for the Lambda execution to complete.
|
|
185
|
+
|
|
186
|
+
**Parameters:**
|
|
187
|
+
|
|
188
|
+
- `functionName`: `string` - The name or ARN of the Lambda function to invoke
|
|
189
|
+
- `payload`: `unknown` - The JSON payload to pass to the Lambda function
|
|
190
|
+
|
|
191
|
+
**Returns:** `Promise<void>` - Promise that resolves when the invocation request is accepted
|
|
192
|
+
|
|
193
|
+
**Throws:** `Error` - If the Lambda invocation request fails
|
|
194
|
+
|
|
195
|
+
### resetLambdaClient()
|
|
196
|
+
|
|
197
|
+
Resets the Lambda client instance. Useful for testing or when you need to reinitialize the client with a different configuration.
|
|
198
|
+
|
|
199
|
+
**Returns:** `void`
|
|
200
|
+
|
|
201
|
+
## Error Handling
|
|
202
|
+
|
|
203
|
+
Both `invokeLambdaSync` and `invokeLambdaAsync` throw errors in the following cases:
|
|
204
|
+
|
|
205
|
+
1. **Function Errors**: When the invoked Lambda function returns an error (e.g., unhandled exceptions)
|
|
206
|
+
2. **AWS SDK Errors**: When the AWS SDK encounters an error (e.g., network issues, permission errors)
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { invokeLambdaSync } from '@leanstacks/lambda-utils';
|
|
210
|
+
|
|
211
|
+
export const handler = async (event: any) => {
|
|
212
|
+
try {
|
|
213
|
+
const response = await invokeLambdaSync('my-function', { key: 'value' });
|
|
214
|
+
return {
|
|
215
|
+
statusCode: 200,
|
|
216
|
+
body: JSON.stringify(response),
|
|
217
|
+
};
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('Lambda invocation failed:', error);
|
|
220
|
+
return {
|
|
221
|
+
statusCode: 500,
|
|
222
|
+
body: JSON.stringify({ error: 'Failed to invoke Lambda function' }),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Testing
|
|
229
|
+
|
|
230
|
+
The utility provides a `resetLambdaClient()` function for testing purposes:
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { resetLambdaClient, initializeLambdaClient } from '@leanstacks/lambda-utils';
|
|
234
|
+
import { mockClient } from 'aws-sdk-client-mock';
|
|
235
|
+
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
|
|
236
|
+
|
|
237
|
+
describe('My Lambda Tests', () => {
|
|
238
|
+
const lambdaClientMock = mockClient(LambdaClient);
|
|
239
|
+
|
|
240
|
+
beforeEach(() => {
|
|
241
|
+
resetLambdaClient();
|
|
242
|
+
lambdaClientMock.reset();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should invoke Lambda function successfully', async () => {
|
|
246
|
+
// Mock the Lambda client response
|
|
247
|
+
const responsePayload = { result: 'success' };
|
|
248
|
+
const encoder = new TextEncoder();
|
|
249
|
+
const responseBytes = encoder.encode(JSON.stringify(responsePayload));
|
|
250
|
+
|
|
251
|
+
lambdaClientMock.on(InvokeCommand).resolves({
|
|
252
|
+
StatusCode: 200,
|
|
253
|
+
Payload: responseBytes,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Test your code that uses invokeLambdaSync or invokeLambdaAsync
|
|
257
|
+
// ...
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Best Practices
|
|
263
|
+
|
|
264
|
+
1. **Singleton Pattern**: The Lambda client is created once and reused across invocations, improving performance by reducing initialization overhead.
|
|
265
|
+
|
|
266
|
+
2. **Error Handling**: Always wrap Lambda invocations in try-catch blocks to handle potential errors gracefully.
|
|
267
|
+
|
|
268
|
+
3. **Type Safety**: Use TypeScript generics to specify the expected response type for synchronous invocations:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const response = await invokeLambdaSync<MyResponseType>('my-function', payload);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
4. **Async vs Sync**: Choose the appropriate invocation type:
|
|
275
|
+
- Use `invokeLambdaSync` when you need to wait for and process the response
|
|
276
|
+
- Use `invokeLambdaAsync` for fire-and-forget scenarios to improve performance
|
|
277
|
+
|
|
278
|
+
5. **IAM Permissions**: Ensure your Lambda function has the necessary IAM permissions to invoke other Lambda functions:
|
|
279
|
+
```json
|
|
280
|
+
{
|
|
281
|
+
"Effect": "Allow",
|
|
282
|
+
"Action": ["lambda:InvokeFunction"],
|
|
283
|
+
"Resource": "arn:aws:lambda:region:account-id:function:function-name"
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## See Also
|
|
288
|
+
|
|
289
|
+
- [AWS SDK for JavaScript - Lambda Client](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-lambda/)
|
|
290
|
+
- [AWS Lambda Developer Guide - Invoking Functions](https://docs.aws.amazon.com/lambda/latest/dg/lambda-invocation.html)
|
|
291
|
+
- [DynamoDB Client Documentation](./DYNAMODB_CLIENT.md)
|
|
292
|
+
- [SNS Client Documentation](./SNS_CLIENT.md)
|
package/docs/README.md
CHANGED
|
@@ -8,18 +8,20 @@ Lambda Utilities is a collection of pre-configured tools and helpers designed to
|
|
|
8
8
|
|
|
9
9
|
## Documentation
|
|
10
10
|
|
|
11
|
+
- **[Configuration Guide](./CONFIGURATION.md)** – Validate environment variables with Zod schemas and type-safe configuration management
|
|
11
12
|
- **[Logging Guide](./LOGGING.md)** – Implement structured logging in your Lambda functions with Pino and automatic AWS context enrichment
|
|
12
13
|
- **[API Gateway Responses](./API_GATEWAY_RESPONSES.md)** – Format Lambda responses for API Gateway with standard HTTP status codes and headers
|
|
13
14
|
- **[DynamoDB Client](./DYNAMODB_CLIENT.md)** – Reusable singleton DynamoDB client instances with custom configuration
|
|
14
|
-
- **[
|
|
15
|
-
- **[
|
|
15
|
+
- **[Lambda Client](./LAMBDA_CLIENT.md)** – Reusable singleton Lambda client for invoking other Lambda functions
|
|
16
|
+
- **[SNS Client](./SNS_CLIENT.md)** – Reusable singleton SNS client for publishing messages to topics with message attributes
|
|
17
|
+
- **[SQS Client](./SQS_CLIENT.md)** – Reusable singleton SQS client for sending messages to queues with message attributes
|
|
16
18
|
|
|
17
19
|
## Features
|
|
18
20
|
|
|
19
21
|
- 📝 **Structured Logging** – Pino logger pre-configured for Lambda with automatic request context
|
|
20
22
|
- 📤 **API Response Helpers** – Standard response formatting for API Gateway integration
|
|
21
23
|
- ⚙️ **Configuration Validation** – Environment variable validation with Zod schema support
|
|
22
|
-
- 🔌 **AWS Clients** – Pre-configured AWS SDK v3 clients for
|
|
24
|
+
- 🔌 **AWS Clients** – Pre-configured AWS SDK v3 clients for DynamoDB, SNS, SQS, and Lambda
|
|
23
25
|
- 🔒 **Type Safe** – Full TypeScript support with comprehensive type definitions
|
|
24
26
|
|
|
25
27
|
## Support
|