npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@3billion_dev/secrets-manager-module

v1.2.0

Published

A NestJS module for using AWS Secrets Manager

Readme

@3billion_dev/secrets-manager-module

AWS Secrets Manager 값을 가져오고 검증하여 애플리케이션에서 쉽게 사용할 수 있게 해 주는 가벼운 NestJS 모듈입니다.

기능:

  • forRoot / forRootAsync 모듈 등록 방식 지원
  • forFeature({ name, ... }) 다중 클라이언트 구성 지원(각기 다른 Secret/Region 동시 사용)
  • AWS SDK SecretsManagerClient를 DI로 주입(테스트 시 클라이언트 모킹 용이)
  • 커스텀 Zod 스키마 검증(검증 실패 시 항상 예외 발생, 원인 포함)
  • 메모리 스냅샷 보관 및 타입 안전한 get/getAll, dot-path 접근(lodash.get), getOrThrow 지원
  • 모듈 시작 시 자동 초기화(onModuleInit)

설치

pnpm add @3billion_dev/secrets-manager-module @aws-sdk/client-secrets-manager zod lodash

필수 피어 의존성:

  • @nestjs/common (v11)
  • zod (v3)

사내 프라이빗 GitHub Packages 레지스트리를 사용한다면, 프로젝트의 .npmrc에 유효한 토큰이 설정되어 있어야 합니다.

빠른 시작

import { Module } from "@nestjs/common";
import {
  SecretsManagerModule,
  getServiceToken,
} from "@3billion_dev/secrets-manager-module";

import { z } from "zod";

// Zod 스키마 예시
const schema = z.object({
  DATABASE_URL: z.string().min(1, "DATABASE_URL is required"),
});

@Module({
  imports: [
    SecretsManagerModule.forRoot({
      isGlobal: true, // 선택 사항: 전역 모듈로 등록
      secretName: "prod/app/secrets",
      clientConfig: { region: "ap-northeast-2" }, // 선택 사항; 생략 시 런타임 자격 증명 사용
      schema,
    }),
  ],
})
export class AppModule {}

DI를 통해 시크릿 사용하기(기본 인스턴스):

import { Inject, Injectable } from "@nestjs/common";
import {
  SecretsManagerService,
  InjectService,
} from "@3billion_dev/secrets-manager-module";

type SecretsShape = { DATABASE_URL: string; http: { timeout: number } };

@Injectable()
export class AnyService {
  constructor(
    @InjectService()
    private readonly secrets: SecretsManagerService<SecretsShape>
  ) {}

  async use() {
    const url = await this.secrets.get("DATABASE_URL");
    const timeout = await this.secrets.get<number>("http.timeout", 5000);
    const required = await this.secrets.getOrThrow("DATABASE_URL");
  }
}

여러 클라이언트 구성(forFeature)

서로 다른 Secret/Region을 동시에 사용해야 할 때는 forFeature를 사용합니다.

import { Module, Inject, Injectable } from "@nestjs/common";
import {
  SecretsManagerModule,
  SecretsManagerService,
  getServiceToken,
} from "@3billion_dev/secrets-manager-module";

@Module({
  imports: [
    SecretsManagerModule.forFeature({
      name: "payments",
      secretName: "payments/secrets",
      clientConfig: { region: "us-east-1" },
    }),
    SecretsManagerModule.forFeature({
      name: "analytics",
      secretName: "analytics/secrets",
      clientConfig: { region: "eu-central-1" },
    }),
  ],
})
export class FeatureModule {}

@Injectable()
export class UsesMultiple {
  constructor(
    @InjectService("payments")
    private readonly paymentsSecrets: SecretsManagerService<any>,
    @InjectService("analytics")
    private readonly analyticsSecrets: SecretsManagerService<any>
  ) {}
}

비동기 설정

import { ConfigModule, ConfigService } from "@nestjs/config";

SecretsManagerModule.forRootAsync({
  isGlobal: true, // 선택 사항: 전역 모듈로 등록 (비동기 등록 시 이 레벨에서 설정)
  imports: [ConfigModule],
  useFactory: async (cfg: ConfigService) => ({
    secretName: cfg.get<string>("SECRET_NAME")!,
    clientConfig: { region: cfg.get<string>("AWS_REGION") },
    schema, // 선택 사항이지만 권장
  }),
  inject: [ConfigService],
});

DI 토큰

여러 인스턴스/명명된 인스턴스를 지원하기 위해 다음 토큰 헬퍼를 제공합니다.

  • getOptionsToken(name?)
  • getClientToken(name?) – AWS SecretsManagerClient
  • getServiceToken(name?) – SecretsManagerService

기본 인스턴스는 name을 생략하고, 특징별 인스턴스는 name을 지정합니다.

DI 데코레이터

  • InjectService(name?: string)
  • InjectClient(name?: string)
  • InjectOptions(name?: string)

API

모듈 옵션 (forRoot/forRootAsync):

  • isGlobal?: boolean – 모듈을 전역으로 등록할지 여부 (기본값: false)
  • secretName: string (필수) – AWS Secrets Manager Secret ID
  • clientConfig?: SecretsManagerClientConfig – SecretsManagerClient에 전달되는 옵션
  • schema?: z.ZodSchema<unknown> – Zod 스키마(유효하지 않으면 예외 발생)

스키마 타입: z.ZodSchema<YourSecretsType>

forFeature 옵션:

  • name: string (필수)
  • 나머지 필드 동일(secretName, clientConfig, schema)

참고:

  • forFeature는 특정 명명 인스턴스를 추가로 등록하기 위한 서브 모듈입니다. isGlobal은 지원하지 않으며 필요하지도 않습니다. 전역 등록이 필요한 경우 forRoot 또는 forRootAsync에서 isGlobal: true를 설정하세요.
  • 현재 forFeatureAsync는 제공되지 않습니다. 비동기 구성이 필요한 경우 forRootAsync를 사용하고, 필요 시 추가 인스턴스는 동기 forFeature로 등록하세요.
  • forRootAsync의 isGlobal은 최상위 옵션(팩토리 반환값이 아닌 forRootAsync({...})의 옵션)에 설정해야 적용됩니다.
  • service 내의 secret 값들은 get/getAll/getOrThrow 매서드 호출 시에 값이 없는 경우에만 secrets manager에서 값을 가져옵니다.

서비스 메서드:

  • initialize(): Promise<void> – 시크릿을 가져와 검증 (모듈 초기화 시 자동 실행)
  • getAll(): TSecrets – 검증된 전체 맵 반환
  • get<TKey extends keyof TSecrets>(key: TKey): TSecrets[TKey] – 최상위 키 접근
  • get<TValue = unknown>(path: string, defaultValue?: TValue): TValue – dot-path로 중첩 값 접근(lodash.get 사용)
  • getOrThrow<TKey extends keyof TSecrets>(key: TKey): TSecrets[TKey] – 필수 최상위 키, 없으면 예외
  • getOrThrow<TValue = unknown>(path: string): TValue – 필수 dot-path, 없으면 예외

타이핑 팁:

  • 모듈 등록은 제네릭을 요구하지 않습니다. 대신 DI 시점에 SecretsManagerService<YourSecretsType>으로 타입을 지정해 안전하게 사용하세요.

참고:

  • 검증 실패 시 항상 예외가 발생합니다.
  • 최초 로드 후 시크릿은 메모리에 보관됩니다. 로테이션이 필요하면 서비스 확장으로 갱신 로직을 추가하세요.
  • AWS 자격 증명은 clientConfig.credentials를 명시하지 않으면 AWS SDK v3 기본 프로바이더 체인을 따릅니다.
  • 중첩 경로 접근은 lodash.get으로 처리합니다.

테스트

클라이언트를 DI로 주입하므로, 테스트에서 네트워크 호출 없이 간단히 모킹할 수 있습니다.

import { Test } from "@nestjs/testing";
import {
  getOptionsToken,
  getClientToken,
  SecretsManagerModuleOptions,
  SecretsManagerService,
} from "@3billion_dev/secrets-manager-module";

type SecretsShape = { TOKEN: string; nested: { url: string } };

const secrets: SecretsShape = {
  TOKEN: "abc",
  nested: { url: "postgres://localhost" },
};

const mockClient = {
  send: jest.fn().mockResolvedValue({
    SecretString: JSON.stringify(secrets),
  }),
};

const options: SecretsManagerModuleOptions = { secretName: "dummy" };

const moduleRef = await Test.createTestingModule({
  providers: [
    { provide: getOptionsToken(), useValue: options },
    { provide: getClientToken(), useValue: mockClient },
    SecretsManagerService<SecretsShape>,
  ],
}).compile();

await moduleRef.init();

const svc = moduleRef.get(SecretsManagerService<SecretsShape>);
expect(svc.get("TOKEN")).toBe("abc");
expect(svc.get<string>("nested.url")).toBe("postgres://localhost");

라이선스

MIT