t3-envでTypesafeに環境変数を使いたい

# はじめに
普段`.env`ファイルを用いて環境変数の設定をしているのですが、`process.env`オブジェクトで環境変数にアクセスすると型の問題や環境変数の設定漏れが実行時にしかわからない問題があったため、**t3-env**というOSSを使ってこの問題を解決しようと思います。

## t3-envとは？
> Forgetting environment variables during build can be a hassle and difficult to debug if a bug is caused by a missing environment variable. This package provides a simple way to define environment variables validation for your app.
This library does all the grunt work for you, simply define your schema and use your environment variables safely.

環境変数の設定漏れやprocess.envを使用することによる型の問題を解決するためのOSSです。Theo 氏によって開発された`T3 Stack`というWeb開発技術スタックの中の環境変数を操作する部分をOSS化したものになります。


## 注意事項
- bunを使っていますがパッケージマネージャーは適宜読み替えてください
- 今回は特定のフレームワークには依存せず`env-core`というパッケージを使用します
  - Next.jsやNuxtへの導入方法は公式ドキュメントの方で記載があります 
- t3-envはESMモジュールでしか使えないのでCommonJSの人は注意が必要です

# setup方法
## install
`zod`に依存関係があるので一緒にインストールして下さい
```
$ bun add @t3-oss/env-core zod
```

## env.tsファイルの準備
t3-envを使って環境変数を一括で管理していきます。こちらのファイルに書いてある環境変数が`.env`ファイル等から取得できない場合に、例外が起きるようになります。
```
# env.ts
import { createEnv } from '@t3-oss/env-core';
import { z } from 'zod';

export const env = createEnv({
    // フロントで動かしたい場合はclientにする
	server: {
        // ここに環境変数を書いていく
		DATABASE_URL: z.string(),
	},
	runtimeEnv: process.env,
});

```
### tsconfigの設定
env.tsを書くとTypescriptから下記のようなエラーが発生すると思います。
```
# env.ts
import { createEnv } from '@t3-oss/env-core';

error:
Cannot find module '@t3-oss/env-core' or its corresponding type declarations.
  There are types at '/workspace/node_modules/@t3-oss/env-core/dist/index.d.ts', but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'.ts(2307)
```
`tsconfig.json`にpathを書いてエラーを回避していきます
```
# tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    // pathsを追加する
    "paths": {
      "@t3-oss/env-core": [
        "./node_modules/@t3-oss/env-core/dist"
      ]
    }
  }
}

```

## Linterの設定
環境変数を`process.env`経由で取得して欲しくないのでLinterで`をprocess.env`を使用した時にwarningを出すようにします
自分はbiomeを使っているので`biome.jsonc`を編集していきます。
```
# biome.jsonc
{
  "linter": {
    "enabled": true,
	"rules": {
      "recommended": true,
		"nursery": {
          // 厳格にしたいなら"error"にしてください
		  "noProcessEnv": "warn"
		}
	}
  },
}
```
`env.ts`だけは設定上`process.env`を使う必要があるのでignoreします
```
# env.ts
import { createEnv } from '@t3-oss/env-core';
import { z } from 'zod';

export const env = createEnv({
	server: {
		DATABASE_URL: z.string(),
	},
    // 追加する ///////////////////////
	// biome-ignore lint: noProcessEnv
	runtimeEnv: process.env,
});
```

# 使い方
`process.env`の代わりに先ほど作成した`env.ts`から`env`オブジェクトをimportして使用していきます。
`process.env`だと型アサーションする必要があるかと思いますがt3-envはzodで型を指定済みなので型アサーションする必要はありません。また、`env`オブジェクトが環境変数の情報を持っているのでIDEでの型補完が効きます。
```
# process.envの場合
console.log(process.env.DATABASE_URL as string) // undefinedになる可能性があるため型アサーションが必要

# t3-envのenvオブジェクトの場合
import { env } from './env';
console.log(env.DATABASE_URL)  // 型アサーション必要なし
```

# 終わりに
ビルド時などに環境変数が足りない、なんてことが起こらないといいなと思ってお守り代わりに使って行きたいと思います。

# 参考文献
https://create.t3.gg/

https://github.com/t3-oss/t3-env

https://env.t3.gg/docs/core

https://tech-lab.sios.jp/archives/44133

https://zenn.dev/hayato94087/books/6a55108faa37ba/viewer/g060s56pd8o5jneg