AWS resolvers for node-env-resolver (Secrets Manager, SSM Parameter Store)
npm install node-env-resolver-awsAWS integration for node-env-resolver with Secrets Manager and SSM Parameter Store support.

``bash`
npm install node-env-resolver/aws
`ts
import { resolveSsm, resolveSecrets } from 'node-env-resolver-aws';
// Resolve from SSM Parameter Store
const config = await resolveSsm({
path: '/myapp/config'
}, {
API_ENDPOINT: string(),
TIMEOUT: 30
});
// Resolve from Secrets Manager
const secrets = await resolveSecrets({
secretId: 'myapp/production/secrets'
}, {
DATABASE_URL: string(),
API_KEY: string()
});
`
`ts
import { resolveAsync } from 'node-env-resolver';
import { awsSecrets, awsSsm } from 'node-env-resolver-aws';
const config = await resolveAsync({
resolvers: [
[awsSecrets({ secretId: 'myapp/production/secrets' }), {
DATABASE_URL: postgres(),
API_KEY: string(),
}],
[awsSsm({ path: '/myapp/config' }), {
TIMEOUT: 30,
}]
]
});
`
- One-line convenience functions for quick setup
- Load secrets from AWS Secrets Manager
- Load parameters from SSM Parameter Store
- Safe (non-throwing) versions of all functions
- Automatic AWS credential detection (environment variables, IAM roles, ~/.aws/credentials)
- Optional TTL caching
- Full TypeScript support
AWS API calls are slow (~100-200ms) and cost money. Caching is essential for production applications.
`typescript`
// Every request hits AWS = slow and expensive
export const handler = async (event) => {
const config = await resolveSecrets({
secretId: 'myapp/secrets'
}, {
DATABASE_URL: postgres(),
API_KEY: string()
});
// 200ms delay on EVERY request + AWS API costs
};
`typescript
import { resolveAsync, cached, TTL } from 'node-env-resolver';
import { awsSecrets } from 'node-env-resolver-aws';
// Cache AWS calls - call resolveAsync() every time, let cached() make it fast
export const getConfig = async () => {
return await resolveAsync({
resolvers: [
[cached(
awsSecrets({ secretId: 'myapp/secrets' }),
{
ttl: TTL.minutes5,
maxAge: TTL.hour,
staleWhileRevalidate: true
}
), {
DATABASE_URL: postgres(),
API_KEY: string(),
}]
]
});
};
// Call in your handler
export const handler = async (event) => {
const config = await getConfig(); // Fast after first call!
// Use config...
};
`
AWS Lambda:
`typescript
import { resolveAsync, cached, TTL } from 'node-env-resolver';
import { awsSecrets } from 'node-env-resolver-aws';
const getConfig = async () => {
return await resolveAsync({
resolvers: [
[cached(
awsSecrets({ secretId: 'myapp/lambda' }),
{ ttl: TTL.minutes5, staleWhileRevalidate: true }
), {
DATABASE_URL: postgres(),
}]
]
});
};
export const handler = async (event) => {
const config = await getConfig(); // Call every invocation
// Lambda container reuse = cache persists across invocations
// = most invocations get instant config
};
`
1. Always use cached() wrapper when accessing AWS Secrets Manager or SSM
2. Call resolveAsync() every time - don't cache the result in a variable
3. Use staleWhileRevalidate: true for zero-latency updates
4. Choose appropriate TTL based on how often secrets change:
- Frequently rotating: TTL.minutes5TTL.hour
- Rarely changing: or TTL.hours6maxAge
5. Set as a safety net (default: 1 hour)
This package uses the standard AWS SDK credential provider chain. Credentials are automatically detected from:
1. Environment variables (recommended for local development)
`bash`
export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
export AWS_REGION=us-west-2
2. IAM roles (recommended for production - EC2, Lambda, ECS)
3. AWS credentials file (~/.aws/credentials)
4. Explicit options (for special cases):
`ts`
await resolveSsm({
path: '/myapp/config',
region: 'us-east-1',
accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
}, { API_ENDPOINT: string() });
In most cases, you don't need to pass credentials explicitly - just set the standard AWS environment variables or use IAM roles.
#### resolveSsm(ssmOptions, schema, resolveOptions?)
Directly resolve environment variables from SSM Parameter Store.
`ts
import { resolveSsm } from 'node-env-resolver-aws';
const config = await resolveSsm({
path: '/myapp/config',
region: 'us-east-1',
recursive: true
}, {
API_ENDPOINT: string(),
TIMEOUT: 30
});
`
#### safeResolveSsm(ssmOptions, schema, resolveOptions?)
Safe version that returns a result object instead of throwing.
`ts
import { safeResolveSsm } from 'node-env-resolver-aws';
const result = await safeResolveSsm({
path: '/myapp/config'
}, {
API_ENDPOINT: string()
});
if (result.success) {
console.log(result.data.API_ENDPOINT);
} else {
console.error(result.error);
}
`
#### resolveSecrets(secretsOptions, schema, resolveOptions?)
Directly resolve environment variables from Secrets Manager.
`ts
import { resolveSecrets } from 'node-env-resolver-aws';
const config = await resolveSecrets({
secretId: 'myapp/production/secrets',
region: 'us-east-1'
}, {
DATABASE_URL: string(),
API_KEY: string()
});
`
#### safeResolveSecrets(secretsOptions, schema, resolveOptions?)
Safe version that returns a result object instead of throwing.
`ts
import { safeResolveSecrets } from 'node-env-resolver-aws';
const result = await safeResolveSecrets({
secretId: 'myapp/secrets'
}, {
DATABASE_URL: string()
});
if (result.success) {
console.log(result.data.DATABASE_URL);
} else {
console.error(result.error);
}
`
#### awsSsm(options) and awsSecrets(options)
These return resolver objects for use with resolveAsync() when you need to combine multiple sources.
Load JSON secrets from Secrets Manager:
`ts
import { resolveAsync } from 'node-env-resolver';
import { awsSecrets } from 'node-env-resolver-aws';
const config = await resolveAsync({
resolvers: [
[awsSecrets({ secretId: 'myapp/secrets' }), {
DATABASE_URL: postgres(),
API_KEY: string()
}]
]
});
`
With options:
`ts`
awsSecrets({
secretId: 'myapp/production/database',
region: 'us-east-1',
parseJson: true // Default: true
})
Load parameters from Parameter Store:
`ts
import { resolveAsync } from 'node-env-resolver';
import { awsSsm } from 'node-env-resolver-aws';
const config = await resolveAsync({
resolvers: [
[awsSsm({ path: '/myapp/config' }), {
API_ENDPOINT: string(),
TIMEOUT: 30
}]
]
});
`
Get all parameters under a path:
`ts`
awsSsm({
path: '/myapp/production',
region: 'us-west-2',
recursive: true
})
Add TTL caching to reduce AWS API calls:
`ts
import { resolveAsync, cached, TTL } from 'node-env-resolver';
import { awsSecrets } from 'node-env-resolver-aws';
const config = await resolveAsync({
resolvers: [
[cached(
awsSecrets({ secretId: 'myapp/secrets' }),
{
ttl: TTL.minutes5,
maxAge: TTL.hour,
staleWhileRevalidate: true
}
), {
DATABASE_URL: postgres()
}]
]
});
`
`json`
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": ["arn:aws:secretsmanager:region:account:secret:myapp/secrets-*"]
}
]
}
`json`
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
],
"Resource": ["arn:aws:ssm:region:account:parameter/myapp/*"]
}
]
}
`ts`
interface AwsSecretsOptions {
secretId: string; // Secret ID or ARN
region?: string; // AWS region (overrides profile/env defaults)
accessKeyId?: string; // AWS access key (optional)
secretAccessKey?: string; // AWS secret key (optional)
parseJson?: boolean; // Parse JSON secrets (default: true)
}
`ts`
interface AwsSsmOptions {
path: string; // Parameter path
region?: string; // AWS region (overrides profile/env defaults)
accessKeyId?: string; // AWS access key (optional)
secretAccessKey?: string; // AWS secret key (optional)
recursive?: boolean; // Get all parameters under path (default: false)
}
`ts
import { resolveSecrets, resolveSsm } from 'node-env-resolver-aws';
// Load secrets from Secrets Manager
const secrets = await resolveSecrets({
secretId: 'myapp/production/secrets'
}, {
DATABASE_URL: postgres(),
JWT_SECRET: string(),
});
// Load config from SSM
const config = await resolveSsm({
path: '/myapp/production/config',
recursive: true
}, {
NODE_ENV: ['development', 'production'] as const,
PORT: 3000,
});
`
`ts
import { resolveAsync, processEnv } from 'node-env-resolver';
import { awsSecrets, awsSsm } from 'node-env-resolver-aws';
const config = await resolveAsync({
resolvers: [
[processEnv(), {
NODE_ENV: ['development', 'production'] as const,
PORT: 3000,
}],
[awsSecrets({ secretId: 'myapp/production/secrets' }), {
DATABASE_URL: postgres(),
JWT_SECRET: string(),
}],
[awsSsm({ path: '/myapp/production/config' }), {
TIMEOUT: 30,
}]
]
});
`
`ts
import { resolveSecrets } from 'node-env-resolver-aws';
const config = await resolveSecrets({
secretId: 'lambda/secrets'
}, {
API_ENDPOINT: string(),
TIMEOUT: 30,
});
export const handler = async (event) => {
// Use config
};
`
`ts
import { safeResolveSecrets } from 'node-env-resolver-aws';
const result = await safeResolveSecrets({
secretId: 'myapp/production/secrets'
}, {
DATABASE_URL: string(),
API_KEY: string()
});
if (result.success) {
// Use result.data with full type safety
await connectDatabase(result.data.DATABASE_URL);
} else {
// Handle error gracefully
console.error('Failed to load secrets:', result.error);
process.exit(1);
}
`
`ts
import { safeResolveSecrets } from 'node-env-resolver-aws';
const result = await safeResolveSecrets({
secretId: 'myapp/secrets'
}, {
DATABASE_URL: string()
});
if (!result.success) {
console.error('Failed to load secrets:', result.error);
process.exit(1);
}
// Use result.data safely
console.log(result.data.DATABASE_URL);
`
`ts
import { resolveSecrets } from 'node-env-resolver-aws';
try {
const config = await resolveSecrets({
secretId: 'myapp/secrets'
}, {
DATABASE_URL: string()
});
} catch (error) {
console.error('Failed to load secrets from AWS:', error);
}
``
Permission denied
- Check IAM permissions match examples above
- Verify resource ARNs are correct
Secret not found
- Verify secret ID or path exists
- Check region matches where secret is stored
Network timeout
- Check VPC/security group configuration
- Verify internet/NAT gateway access
MIT