Skip to main content

AWS Secret Manager

AWS Secret Manager is a great tool to provide secure storage for your secrets like database passwords, JWT secret keys, API keys. Additionally it can be customized to store structures instead of just strings.

Ability to rotate the secrets is a great feature that allows to keep your secrets secure in case of a leak.

Installation​

npm install @pallad/config-aws-secret-manager

Retrieving secrets​

You can retrieve entire secret by providing its name:

import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';

const secretManager = secretManagerProviderFactory();

// retrieves entire "database" secret upon resolution
secretManager({name: 'database'})

Just a property from structured secret​

import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';

const secretManager = secretManagerProviderFactory();

// retrieves "database" secret and extracts property `username` from it
secretManager({name: 'database', property: 'username'})

Ensuring type safety​

All values fetched from secret manager are treated as unknown therefore extra validation is required to ensure type safety.

For property​

import {type} from '@pallad/config';

// ensures that `username` property is a string
secretManager({name: 'database', property: 'username'}).transform(type.string);

// port to be an integer
secretManager({name: 'database', property: 'port'}).transform(type.int);

// custom validation
secretManager({name: 'database', property: 'password'}).transform((value) => {
if (someTypeCheck(value)) {
return value;
}
throw new Error('Invalid value');
});

For structure​

Transform properties for type safety is easy but it can be cumbersome for complex structures therefore using transform on entire secret might recommended.

import {type} from '@pallad/config';

import {z} from 'zod';

const databaseSchema = z.object({
username: z.string(),
password: z.string(),
port: z.number()
});

// ensures that entire secret is an object satisfying `databaseSchema`
secretManager({name: 'database'}).transform((value) => {
return databaseSchema.parse(value);
});

Customization​

Prefix all secret names​

import { secretManagerProviderFactory } from '@pallad/config-aws-secret-manager';

const secretManager = secretManagerProviderFactory({
prefix: '/env/prod/'
});

// retrieves "/env/prod/database" secret
secretManager('database')

Use custom instance of Secret Manager client​

import { ssmProviderFactory } from '@pallad/config-aws-ssm';
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";

const secretManager = secretManagerProviderFactory({
client: new SecretsManagerClient({region: 'us-east-1'})
});
// retrieves "database" secret from `us-east-1` region
secretManager('database')

Deserialization​

By default, all secrets are stored as strings. If data looks like a JSON then it is being deserialized. If you need more control over deserialization you can provide custom deserializer.

import { SecretValueEntry } from "@aws-sdk/client-secrets-manager";

const secretManager = secretManagerProviderFactory({
deserialize(secret: SecretValueEntry) {
if (secret.SecretString) {
return customJSONDeserializer(secret.SecretString);
}

throw new Error('Unsupported secret format');
}
});