A modern, TypeScript-first AWS Secrets Manager client built on AWS SDK v3. This package provides a simplified, promise-based interface for managing secrets with comprehensive error handling, logging, and type safety.
- Modern AWS SDK v3: Built on the latest AWS SDK with improved performance and modularity
- TypeScript First: Full TypeScript support with comprehensive type definitions
- Async/Await: Promise-based API with async/await support (no more callback hell!)
- Comprehensive Error Handling: Detailed error handling with custom error types
- Logging Support: Built-in logging with configurable log levels
- Health Checks: Built-in health check functionality
- Type-Safe JSON Parsing: Automatically parse JSON secrets with type safety
- Retry Logic: Configurable retry policies with exponential backoff
- Zero Dependencies: Only peer dependency on AWS SDK v3
npm install aws-secret-manager-client @aws-sdk/client-secrets-managerimport { SecretsManagerClient } from 'aws-secret-manager-client';
// Initialize the client
const client = new SecretsManagerClient({
region: 'us-east-1',
retryPolicy: {
maxRetries: 3,
},
});
// Get a secret
const secret = await client.getSecret('my-app/database/credentials');
console.log(secret.value);
// Get and parse JSON secret with type safety
interface DatabaseConfig {
host: string;
port: number;
username: string;
password: string;
}
const dbConfig = await client.getSecretJson<DatabaseConfig>('my-app/database/config');
console.log(`Connecting to ${dbConfig.host}:${dbConfig.port}`);interface SecretsManagerOptions {
region?: string;
retryPolicy?: {
maxRetries?: number;
retryDelayOptions?: {
base?: number;
customBackoff?: (retryCount: number) => number;
};
};
timeout?: number;
logger?: {
debug?: (message: string, ...args: any[]) => void;
info?: (message: string, ...args: any[]) => void;
warn?: (message: string, ...args: any[]) => void;
error?: (message: string, ...args: any[]) => void;
};
}Retrieves a secret from AWS Secrets Manager.
const secret = await client.getSecret('my-secret');
console.log(secret.value);
// Get specific version
const secretVersion = await client.getSecret('my-secret', 'version-id');
// Get specific stage
const secretStage = await client.getSecret('my-secret', undefined, 'AWSPENDING');Retrieves and parses a JSON secret with type safety.
interface ApiKeys {
stripe: string;
sendgrid: string;
}
const apiKeys = await client.getSecretJson<ApiKeys>('my-app/api-keys');
console.log(apiKeys.stripe);createSecret(params: CreateSecretCommandInput): Promise<{arn: string; name: string; versionId: string}>
Creates a new secret in AWS Secrets Manager.
const result = await client.createSecret({
Name: 'my-app/new-secret',
Description: 'My new secret',
SecretString: JSON.stringify({ key: 'value' }),
});
console.log(`Created secret: ${result.name}`);updateSecret(params: UpdateSecretCommandInput): Promise<{arn: string; name: string; versionId: string}>
Updates an existing secret.
const result = await client.updateSecret({
SecretId: 'my-app/existing-secret',
SecretString: JSON.stringify({ key: 'new-value' }),
});
console.log(`Updated secret: ${result.name}`);deleteSecret(secretId: string, forceDeleteWithoutRecovery?: boolean, recoveryWindowInDays?: number): Promise<{arn: string; name: string; deletionDate: Date}>
Deletes a secret from AWS Secrets Manager.
// Delete with 30-day recovery window (default)
const result = await client.deleteSecret('my-app/old-secret');
// Force delete without recovery
const result = await client.deleteSecret('my-app/old-secret', true);
// Delete with custom recovery window
const result = await client.deleteSecret('my-app/old-secret', false, 7);rotateSecret(params: RotateSecretCommandInput): Promise<{arn: string; name: string; versionId: string}>
Initiates rotation of a secret.
const result = await client.rotateSecret({
SecretId: 'my-app/database-password',
RotationLambdaARN: 'arn:aws:lambda:us-east-1:123456789012:function:rotate-secret',
});Cancels an in-progress secret rotation.
const result = await client.cancelRotateSecret('my-app/database-password');Performs a health check to verify the client can connect to AWS.
const health = await client.healthCheck();
console.log(`Health status: ${health.status}`);Closes the client connection.
await client.close();The client supports configuration through environment variables:
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_SESSION_TOKEN=your-session-token # For temporary credentialsYour IAM user or role needs the following permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:RotateSecret",
"secretsmanager:CancelRotateSecret"
],
"Resource": "*"
}
]
}import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.Console()
]
});
const client = new SecretsManagerClient({
region: 'us-east-1',
logger: {
debug: logger.debug.bind(logger),
info: logger.info.bind(logger),
warn: logger.warn.bind(logger),
error: logger.error.bind(logger),
},
});import { SecretsManagerError } from 'aws-secret-manager-client';
try {
const secret = await client.getSecret('non-existent-secret');
} catch (error) {
if (error instanceof SecretsManagerError) {
console.log(`Error code: ${error.code}`);
console.log(`Status code: ${error.statusCode}`);
console.log(`Message: ${error.message}`);
switch (error.code) {
case 'RESOURCE_NOT_FOUND':
console.log('Secret not found');
break;
case 'ACCESS_DENIED':
console.log('Access denied');
break;
default:
console.log('Unknown error');
}
} else {
console.log('Unexpected error:', error);
}
}import express from 'express';
import { SecretsManagerClient, SecretsManagerError } from 'aws-secret-manager-client';
const app = express();
const client = new SecretsManagerClient({ region: 'us-east-1' });
app.get('/config', async (req, res) => {
try {
const config = await client.getSecretJson('my-app/config');
res.json({ success: true, data: config });
} catch (error) {
if (error instanceof SecretsManagerError) {
res.status(error.statusCode || 500).json({
success: false,
error: {
code: error.code,
message: error.message,
},
});
} else {
res.status(500).json({
success: false,
error: { message: 'Internal server error' },
});
}
}
});
// Graceful shutdown
process.on('SIGTERM', async () => {
await client.close();
process.exit(0);
});Run the test suite:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- BREAKING: Migrated from AWS SDK v2 to v3
- BREAKING: Removed synchronous methods (all methods are now async)
- BREAKING: Removed
deasyncdependency - Added TypeScript support
- Added comprehensive error handling
- Added logging support
- Added health check functionality
- Added retry logic configuration
- Improved API design with better naming conventions
- Added comprehensive tests with >80% coverage
- Legacy version with AWS SDK v2
- Synchronous and asynchronous methods
- Basic functionality
If you have any questions or need help, please:
- Check the documentation
- Search existing issues
- Create a new issue
Made with β€οΈ by Your Name
