npm package: @samlab-corp/aws-secrets-fetcher
Description: A reusable npm library for fetching environment variables from AWS Secrets Manager
Latest version: 1.1.0

# @samlab-corp/aws-secrets-fetcher

[한국어](./README_KR.md)

A reusable npm library for fetching environment variables from AWS Secrets Manager.

## Features

- ✅ **TypeScript Support** - Full type definitions included
- ✅ **CLI & Programmatic API** - Use as a command-line tool or in your code
- ✅ **Flexible Configuration** - Configure via config file, environment variables, or CLI arguments
- ✅ **Multi-Environment Support** - Fetch secrets for multiple environments at once
- ✅ **AWS Profile Support** - Manage multiple AWS accounts

## Installation

```bash
npm install @samlab-corp/aws-secrets-fetcher
```

### Quick Integration

1. Create `secrets.config.json`:
   ```json
   {
     "secretNamePrefix": "my-app/api",
     "awsRegion": "ap-northeast-2",
     "environments": ["development", "production"]
   }
   ```

2. Add to `package.json`:
   ```json
   {
     "scripts": {
       "secrets:dev": "fetch-secrets development",
       "dev": "npm run secrets:dev && your-dev-command"
     }
   }
   ```

3. Run:
   ```bash
   npm run dev
   ```

This creates `.env.development` with your secrets from AWS Secrets Manager.

## Usage

### 1. CLI Usage

#### Basic Usage

```bash
# Fetch development environment secrets using config file
npx fetch-secrets development

# Fetch all environments
npx fetch-secrets --all

# Override configuration with CLI options
npx fetch-secrets production --prefix my-app/api --region us-west-2

# Use custom config file
npx fetch-secrets development --config ./custom-secrets.config.json
```

#### CLI Options

```
Usage:
  fetch-secrets [environment] [options]

Arguments:
  environment          Target environment (e.g., development, production)

Options:
  --all, -a            Fetch all available environments
  --prefix, -p         AWS Secrets Manager secret name prefix
  --region, -r         AWS region (required if not in config/env)
  --profile            AWS CLI profile name (default: default)
  --output, -o         .env file output directory (default: .)
  --single-env, -s     Output to .env instead of .env.{environment}
  --config, -c         Configuration file path (default: secrets.config.json)
  --help, -h           Show help
  --help, -h           Show help
```

#### CLI Output Behavior

| Command Example | Behavior | Output File | Note |
| :--- | :--- | :--- | :--- |
| `fetch-secrets development` | Fetch `development` environment secrets | `.env.development` | Default behavior |
| `fetch-secrets development -s` | Fetch `development` environment secrets (Single File Mode) | `.env` | **Overwrites existing .env**<br>Useful for deployment |
| `fetch-secrets --all` | Fetch all configured environments | `.env.development`<br>`.env.production`... | Creates multiple files |
| `fetch-secrets --all -s` | ❌ **Error** | - | Cannot use both options together |
| `fetch-secrets -s` | ❌ **Error** | - | Must specify an environment |

### 2. Programmatic API Usage

#### Basic Usage

```typescript
import { SecretsManager } from '@samlab-corp/aws-secrets-fetcher';

// Create SecretsManager instance
const manager = new SecretsManager({
  config: {
    secretNamePrefix: 'my-app/api',
    awsRegion: 'ap-northeast-2',
    outputDir: './env',
  },
});

// Fetch secrets for a specific environment
const result = await manager.fetchEnvironment('development');
console.log(`File created: ${result.filePath}`);
console.log(`Variable count: ${result.secretCount}`);

// Fetch secrets for all environments
const results = await manager.fetchAllEnvironments();
```

#### Using Configuration File

```typescript
import { SecretsManager } from '@samlab-corp/aws-secrets-fetcher';

// Automatically loads secrets.config.json
const manager = new SecretsManager();

// Or specify custom config file path
const manager = new SecretsManager({
  configPath: './custom-secrets.config.json',
});

await manager.fetchEnvironment('production');
```

#### Advanced Usage

```typescript
import { SecretsManager, logger } from '@samlab-corp/aws-secrets-fetcher';

// Disable logging
logger.setEnabled(false);

// Fetch multiple environments sequentially
const manager = new SecretsManager({
  config: {
    secretNamePrefix: 'my-app/api',
    environments: [
      { secretName: 'dev', outputName: 'development' },
      { secretName: 'stg', outputName: 'staging' },
      { secretName: 'prd', outputName: 'production' },
    ],
  },
});

const environments = ['dev', 'stg'];
const results = await manager.fetchMultipleEnvironments(environments);

for (const result of results) {
  console.log(`${result.environment}: ${result.filePath}`);
}
```

## Configuration File

Create a `secrets.config.json` file in your project root:

### Simple Format (Recommended for most cases)

```json
{
  "secretNamePrefix": "my-app/api",
  "awsRegion": "ap-northeast-2",
  "awsProfile": "default",
  "environments": ["development", "local", "production"],
  "outputDir": "."
}
```

### Advanced Format (Custom secretName and outputName)

Use this format when your AWS secret name differs from the output file name:

```json
{
  "secretNamePrefix": "my-app/api",
  "awsRegion": "ap-northeast-2",
  "awsProfile": "default",
  "environments": [
    { "secretName": "dev", "outputName": "development" },
    { "secretName": "local", "outputName": "localhost" },
    { "secretName": "prd", "outputName": "production" }
  ],
  "outputDir": "."
}
```

### Configuration Options

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `secretNamePrefix` | `string` | ✅ | - | AWS Secrets Manager secret name prefix |
| `awsRegion` | `string` | ✅ | - | AWS region (can be set via env `AWS_REGION`) |
| `awsProfile` | `string` | ❌ | `default` | AWS CLI profile name |
| `environments` | `string[]` or `EnvironmentMapping[]` | ❌ | `['development', 'local', 'production']` | Available environments list |
| `outputDir` | `string` | ❌ | `.` | .env file output directory |

#### EnvironmentMapping (Advanced)

| Property | Type | Description |
|----------|------|-------------|
| `secretName` | `string` | Secret name suffix for AWS lookup (`{prefix}/{secretName}`) |
| `outputName` | `string` | Output file name suffix (`.env.{outputName}`) |

## AWS Setup

### 1. Configure AWS Credentials

```bash
aws configure
```

### 2. Create Secrets in AWS Secrets Manager

Secret name format: `{secretNamePrefix}/{environment}`

Examples:
- `my-app/api/development`
- `my-app/api/production`

Store secret values in JSON format:

```json
{
  "DATABASE_URL": "postgresql://localhost:5432/mydb",
  "API_KEY": "your-api-key",
  "SECRET_KEY": "your-secret-key"
}
```

### 3. IAM Permissions

The following IAM permissions are required:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "arn:aws:secretsmanager:*:*:secret:my-app/api/*"
    }
  ]
}
```

## Output Files

Generated `.env` file format:

```env
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=your-api-key
SECRET_KEY=your-secret-key
```

Filename: `.env.{environment}` (e.g., `.env.development`, `.env.production`)

## API Reference

### SecretsManager

Main class for managing secrets.

#### Constructor

```typescript
new SecretsManager(options?: SecretsManagerOptions)
```

#### Methods

- `fetchEnvironment(environment: string): Promise<FetchResult>`
  - Fetch secrets for a specific environment

- `fetchMultipleEnvironments(environments: string[]): Promise<FetchResult[]>`
  - Fetch secrets for multiple environments

- `fetchAllEnvironments(): Promise<FetchResult[]>`
  - Fetch secrets for all environments

- `getConfig(): Readonly<MergedConfig>`
  - Get current configuration

### Types

```typescript
interface SecretsManagerOptions {
  configPath?: string;
  config?: Partial<SecretsConfig>;
}

interface FetchResult {
  environment: string;
  filePath: string;
  secretCount: number;
}

interface EnvironmentMapping {
  secretName: string;   // AWS Secret lookup: {prefix}/{secretName}
  outputName: string;   // Output file: .env.{outputName}
}

interface SecretsConfig {
  secretNamePrefix: string;
  awsRegion?: string;
  awsProfile?: string;
  environments?: (string | EnvironmentMapping)[];  // Supports both formats
  outputDir?: string;
}
```

## Examples

### package.json Scripts

```json
{
  "scripts": {
    "fetch:dev": "fetch-secrets development",
    "fetch:prod": "fetch-secrets production",
    "fetch:all": "fetch-secrets --all"
  }
}
```

### CI/CD Pipeline

```yaml
# GitHub Actions example
- name: Fetch secrets
  env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  run: |
    npx fetch-secrets production --region ap-northeast-2
```

### Programmatic Usage (Build Script)

```typescript
// scripts/fetch-secrets.ts
import { SecretsManager } from '@samlab-corp/aws-secrets-fetcher';

async function main() {
  const manager = new SecretsManager({
    config: {
      secretNamePrefix: process.env.SECRET_PREFIX || 'my-app/api',
      awsRegion: process.env.AWS_REGION || 'ap-northeast-2',
    },
  });

  const env = process.env.NODE_ENV || 'development';
  await manager.fetchEnvironment(env);
}

main().catch(console.error);
```

## Troubleshooting

### Secret Not Found

```
Secret "my-app/api/development" not found in AWS Secrets Manager
```

**Solution:**
1. Verify the secret exists in AWS Console
2. Check that the secret name follows the `{prefix}/{environment}` format
3. Ensure you're using the correct region

### AWS Credentials Error

```
AWS credentials are not configured
```

**Solution:**
```bash
aws configure
```

### Permission Error

```
User is not authorized to perform: secretsmanager:GetSecretValue
```

**Solution:**
Add `secretsmanager:GetSecretValue` permission to your IAM user/role

## Publishing

### For Package Maintainers

To publish a new version to npm:

1. **Update version in package.json**
   ```bash
   npm version patch  # or minor, or major
   ```

2. **Build the package**
   ```bash
   npm run build
   ```

3. **Test the package locally**
   ```bash
   npm pack --dry-run
   ```

4. **Login to npm (if not already logged in)**
   ```bash
   npm login
   ```

5. **Publish to npm**
   ```bash
   npm publish --access public
   ```

### Package Contents

The published package includes:
- `dist/` - Compiled JavaScript and TypeScript definitions
- `examples/` - Usage examples
- `secrets.config.example.json` - Example configuration file
- `README.md` - Documentation
- `LICENSE` - MIT license

Files excluded from the package (via `.npmignore`):
- `src/` - TypeScript source files
- `tsconfig.json` - TypeScript configuration
- Development and test files
- IDE and OS-specific files

## License

MIT

## Contributing

Issues and PRs are welcome!