😱
Secret Managerからシークレットを取得しZodでValidationする関数を書く
最近、Secret Managerから取得したJSONに、zodで定義したスキーマでValidationして型安全に取り扱うコードをよく書くので、関数の引数にzodで定義したスキーマを渡せばValidationしてその型を返す関数を定義して使っている。それを紹介だぜ、という小ネタです。
実装(簡易版)
エラー処理を適当に書いた簡易版です。
import {
GetSecretValueCommand,
SecretsManagerClient,
} from "@aws-sdk/client-secrets-manager";
import type { z, ZodType } from "zod";
export const fetchSecret = async <T extends ZodType<any, any, any>>({
client = new SecretsManagerClient({}),
secretId,
schema,
}: {
client?: SecretsManagerClient;
secretId: string;
schema: T;
}): Promise<z.infer<T>> => {
const output = await client.send(
new GetSecretValueCommand({
SecretId: secretId,
})
);
return schema.parse(JSON.parse(output.SecretString!));
};
使い方
使い方は簡単。
const SecretSchema = z.object({
a: z.string(),
b: z.number(),
c: z.boolean(),
})
const secret = await fetchSecret({
secretId: 'hoge',
schema: SecretSchema,
})
// 型が効くぞ
secret.a;
secret.b;
secret.c;
Secrets Managerに限らず、zodで定義した型で返したい関数全てに応用できると思う。zod、最高。
おまけ: エラー処理を細かく書いた実装
最近はErrorをthrowせずに失敗時もresolveするのが好みなので、そういう実装です。
interface Success<T> {
success: true;
value: T;
}
interface Failure {
success: false;
value: {
code: string;
cause: unknown;
};
}
type FetchSecretResult<T> = Success<T> | Failure;
export const fetchSecret = async <T extends ZodType<any, any, any>>({
client = new SecretsManagerClient({}),
secretId,
schema,
}: {
client?: SecretsManagerClient;
secretId: string;
schema: T;
}): Promise<FetchSecretResult<z.infer<T>>> => {
let output: GetSecretValueCommandOutput;
try {
output = await client.send(
new GetSecretValueCommand({
SecretId: secretId,
})
);
} catch (cause) {
return {
success: false,
value: { code: "GetSecretValueCommandFailed", cause },
};
}
if (!output.SecretString) {
return {
success: false,
value: { code: "NoSecretString", cause: output },
};
}
let json: unknown;
try {
json = JSON.parse(output.SecretString);
} catch (cause) {
return {
success: false,
value: { code: "JSONParseFailed", cause: output },
};
}
try {
return schema.parse(json);
} catch (cause) {
return {
success: false,
value: { code: "UnexpectedSecret", cause: output },
};
}
};
Discussion
記事の内容を生かしつつ、デモをneverthrowライブラリで作ってみました。
デモコードです。
簡単ですが、以上です。