T-CREATOR

Astro の別環境(dev/stg/prd)を切り替える設定と運用フローの作り方

Astro の別環境(dev/stg/prd)を切り替える設定と運用フローの作り方

Astro プロジェクトを開発していると、ローカル開発環境(dev)、ステージング環境(stg)、本番環境(prd)それぞれで異なる API エンドポイントや設定値を使いたくなりますよね。環境ごとに手動で設定を切り替えるのは面倒ですし、人的ミスも発生しやすいでしょう。

本記事では、Astro プロジェクトにおける複数環境の切り替え設定と、それを運用する実践的なフローを解説します。環境変数の管理から、ビルドコマンドの設計、CI/CD との連携まで、初心者の方でも理解できるよう段階的に説明していきますね。

背景

Web 開発における環境分離の重要性

現代の Web 開発では、開発・ステージング・本番という複数の環境を分けて管理するのが一般的です。

各環境には明確な役割があります。開発環境では新機能の実装や実験的な変更を自由に試せますし、ステージング環境では本番と同等の条件で最終確認ができるのです。そして本番環境は、エンドユーザーが実際に利用する安定した環境として機能します。

Astro における環境管理の特性

Astro は静的サイトジェネレーター(SSG)として動作する場合が多く、ビルド時に環境変数を埋め込む仕組みになっています。

つまり、ビルド時点で環境が確定するため、ビルド前に適切な環境変数を設定することが重要なのです。Next.js のようにランタイムで環境を判定するフレームワークとは異なり、Astro ではビルドコマンド実行時に環境を明示的に指定する必要がありますね。

以下の図は、3 つの環境の役割と特徴を示しています。

mermaidflowchart LR
  devEnv["開発環境<br/>(dev)"]
  stgEnv["ステージング環境<br/>(stg)"]
  prdEnv["本番環境<br/>(prd)"]

  devEnv -->|"動作確認完了"| stgEnv
  stgEnv -->|"最終テスト合格"| prdEnv

  devEnv -.->|"特徴"| devNote["ローカル開発<br/>頻繁な変更<br/>開発用API"]
  stgEnv -.->|"特徴"| stgNote["本番同等環境<br/>最終確認<br/>テスト用API"]
  prdEnv -.->|"特徴"| prdNote["エンドユーザー<br/>高い安定性<br/>本番API"]

この図からわかるように、各環境には明確な目的があり、段階的に品質を担保していく仕組みになっています。

課題

環境ごとの設定値管理の難しさ

Astro プロジェクトで複数環境を管理しようとすると、いくつかの課題に直面します。

まず、API エンドポイント、認証情報、分析ツールのトラッキング ID など、環境ごとに異なる設定値が多数存在するでしょう。これらをコード内にハードコーディングしてしまうと、環境を切り替えるたびにコードを書き換える必要が出てきます。

ビルドコマンドの複雑化

環境ごとに異なるビルドコマンドを実行する必要がある場合、コマンドが長く複雑になりがちです。

例えば、「開発環境用にビルドする時は NODE_ENV=development を設定して、API_URL には開発用の URL を指定して…」といった具合に、覚えるべき情報が増えてしまうのです。チームメンバー全員が正確にコマンドを実行できるとは限りませんね。

環境変数の誤使用リスク

最も危険なのは、本番環境に開発環境の設定が混入してしまうケースです。

開発用の API キーを本番環境で使ってしまったり、逆に本番の認証情報をローカルで使ってしまったりすると、セキュリティインシデントにつながる可能性があります。手動での切り替えに頼っていると、こうしたヒューマンエラーを防ぐのは困難でしょう。

以下の図は、環境管理が適切でない場合に発生する問題を示しています。

mermaidflowchart TD
  developer["開発者"] -->|"手動で環境変数を変更"| mistake["ヒューマンエラー"]
  mistake -->|"本番に開発設定が混入"| security["セキュリティリスク"]
  mistake -->|"環境の設定忘れ"| buildError["ビルドエラー"]
  mistake -->|"誤った API 使用"| dataLeak["データ漏洩リスク"]

  security -.->|"影響"| impact1["認証情報の漏洩"]
  buildError -.->|"影響"| impact2["デプロイ失敗"]
  dataLeak -.->|"影響"| impact3["テストデータの本番混入"]

このような課題を解決するには、自動化された環境切り替えの仕組みが必要です。

解決策

環境変数ファイルによる設定分離

Astro では .env ファイルを使って環境変数を管理できます。環境ごとに異なるファイルを用意することで、設定を明確に分離できるのです。

標準的なファイル命名規則は以下の通りです。

#ファイル名用途優先度
1.env全環境で共通の設定
2.env.development開発環境専用の設定
3.env.stagingステージング環境専用
4.env.production本番環境専用
5.env.localローカル環境の上書き(Git 管理外)

Astro は自動的に環境に応じた .env ファイルを読み込んでくれますが、カスタム環境名を使う場合は明示的な指定が必要になります。

astro.config.mjs での環境別設定

Astro の設定ファイルでも、環境に応じた設定を切り替えられます。

環境変数を使って設定を動的に変更することで、1 つの設定ファイルで全環境に対応できるのです。例えば、本番環境では圧縮を有効にし、開発環境では無効にするといった制御が可能になりますね。

package.json でのコマンド整備

環境ごとのビルドコマンドを package.jsonscripts セクションに定義しておくと、チームメンバー全員が統一されたコマンドで作業できます。

コマンド名を build:devbuild:stgbuild:prd のように環境名を含めることで、実行時の意図が明確になるでしょう。

以下の図は、環境切り替えの全体フローを示しています。

mermaidflowchart TD
  command["yarn build:dev/stg/prd"] -->|"1. 環境変数設定"| setEnv["NODE_ENV=xxx<br/>APP_ENV=xxx"]
  setEnv -->|"2. .env ファイル読込"| loadEnv[".env.development<br/>.env.staging<br/>.env.production"]
  loadEnv -->|"3. 設定ファイル解釈"| config["astro.config.mjs"]
  config -->|"4. ビルド実行"| build["Astro ビルドプロセス"]
  build -->|"5. 環境別出力"| output["dist/ ディレクトリ<br/>環境変数が埋め込まれた静的ファイル"]

  output -.->|"開発"| devDeploy["localhost:4321"]
  output -.->|"ステージング"| stgDeploy["stg.example.com"]
  output -.->|"本番"| prdDeploy["example.com"]

この図が示すように、コマンド実行から環境変数の設定、ビルド、デプロイまでが一連の流れで処理されます。

具体例

環境変数ファイルの作成

まず、プロジェクトルートに各環境用の .env ファイルを作成します。

すべての環境で共通する設定は .env ファイルに記載しましょう。サイト名やデフォルトの言語設定など、環境に依存しない値を定義します。

env# .env (全環境共通)
PUBLIC_SITE_NAME=My Astro Site
PUBLIC_DEFAULT_LANG=ja

次に、開発環境専用の設定を .env.development に記載します。ローカル開発で使用する API エンドポイントや、開発用の分析ツールの設定を含めましょう。

env# .env.development (開発環境)
NODE_ENV=development
PUBLIC_API_URL=http://localhost:3000/api
PUBLIC_GA_ID=G-XXXXXXXXXX-DEV
PUBLIC_APP_ENV=development
ENABLE_DEBUG=true

ステージング環境用の設定も同様に作成します。本番環境に近い設定値を使いますが、テスト用の API や分析ツールの ID を指定しましょうね。

env# .env.staging (ステージング環境)
NODE_ENV=production
PUBLIC_API_URL=https://stg-api.example.com/api
PUBLIC_GA_ID=G-XXXXXXXXXX-STG
PUBLIC_APP_ENV=staging
ENABLE_DEBUG=false

本番環境の設定では、実際のエンドユーザーが使用する本番用の値を指定します。デバッグモードは必ず無効にしておきましょう。

env# .env.production (本番環境)
NODE_ENV=production
PUBLIC_API_URL=https://api.example.com/api
PUBLIC_GA_ID=G-XXXXXXXXXX-PRD
PUBLIC_APP_ENV=production
ENABLE_DEBUG=false

環境変数名に PUBLIC_ プレフィックスを付けると、クライアントサイドのコードからもアクセスできるようになります。認証情報など秘密にすべき値には、このプレフィックスを付けないように注意してくださいね。

.gitignore の設定

環境変数ファイルのうち、秘密情報を含むものは Git 管理から除外する必要があります。

.gitignore ファイルに以下の設定を追加しましょう。.env.local は個人のローカル設定用なので、必ず除外します。

gitignore# .gitignore
.env.local
.env.*.local

一方で、.env.development.env.staging.env.production はチーム全体で共有する設定なので、Git にコミットしても問題ありません。ただし、実際の API キーや認証情報は含めず、プレースホルダーやダミー値にしておくのが安全です。

astro.config.mjs の環境対応

Astro の設定ファイルで、環境変数を読み込んで動的に設定を変更できます。

まず、必要なモジュールをインポートします。Node.js の loadEnv 関数を使って環境変数を読み込む準備をしましょう。

javascript// astro.config.mjs
import { defineConfig, loadEnv } from 'astro/config';

const { PUBLIC_APP_ENV } = loadEnv(
  process.env.NODE_ENV,
  process.cwd(),
  ''
);

次に、環境に応じて異なる設定を定義します。開発環境では詳細なエラー表示を有効にし、本番環境では最適化を優先する設定にしましょうね。

javascript// 環境判定用の変数
const isProduction = PUBLIC_APP_ENV === 'production';
const isStaging = PUBLIC_APP_ENV === 'staging';
const isDevelopment = PUBLIC_APP_ENV === 'development';

環境ごとの設定を適用します。例えば、本番環境とステージング環境では圧縮を有効にし、開発環境では無効にするといった制御が可能です。

javascriptexport default defineConfig({
  compressHTML: isProduction || isStaging,
  build: {
    inlineStylesheets: isProduction ? 'auto' : 'never',
  },
  vite: {
    build: {
      minify: isProduction,
      sourcemap: !isProduction,
    },
  },
});

この設定により、開発環境ではデバッグがしやすく、本番環境では最適化されたビルド結果が得られます。

package.json のスクリプト設定

環境ごとのビルドコマンドを package.json に定義すると、チーム全体で統一されたコマンドが使えるようになります。

開発、ステージング、本番の各環境用にビルドコマンドを用意しましょう。環境変数を cross-env で設定することで、Windows と Mac/Linux の両方で動作するコマンドになります。

json{
  "scripts": {
    "dev": "astro dev",
    "build:dev": "cross-env PUBLIC_APP_ENV=development astro build",
    "build:stg": "cross-env PUBLIC_APP_ENV=staging astro build",
    "build:prd": "cross-env PUBLIC_APP_ENV=production astro build",
    "preview": "astro preview"
  },
  "devDependencies": {
    "cross-env": "^7.0.3"
  }
}

cross-env パッケージをインストールしていない場合は、以下のコマンドでインストールできますよ。

bashyarn add -D cross-env

プレビュー用のコマンドも用意しておくと、ビルド後の確認が簡単になります。環境ごとにプレビューコマンドを分けることも可能です。

json{
  "scripts": {
    "preview:dev": "cross-env PUBLIC_APP_ENV=development astro preview",
    "preview:stg": "cross-env PUBLIC_APP_ENV=staging astro preview",
    "preview:prd": "cross-env PUBLIC_APP_ENV=production astro preview"
  }
}

コンポーネントでの環境変数使用

作成した環境変数を Astro コンポーネントで使用する例を見てみましょう。

まず、環境変数をインポートします。PUBLIC_ プレフィックスが付いた変数は、import.meta.env からアクセスできるのです。

typescript---
// src/components/ApiClient.astro
const apiUrl = import.meta.env.PUBLIC_API_URL;
const appEnv = import.meta.env.PUBLIC_APP_ENV;
const enableDebug = import.meta.env.ENABLE_DEBUG === 'true';
---

取得した環境変数を使って、環境に応じた処理を実装できます。開発環境では詳細なログを出力し、本番環境では最小限の情報のみを表示するといった制御が可能ですね。

typescript<script>
  const API_URL = import.meta.env.PUBLIC_API_URL;
  const APP_ENV = import.meta.env.PUBLIC_APP_ENV;

  async function fetchData() {
    try {
      const response = await fetch(`${API_URL}/users`);
      const data = await response.json();

      if (APP_ENV === 'development') {
        console.log('API Response:', data);
      }

      return data;
    } catch (error) {
      console.error('API Error:', error);
    }
  }
</script>

型安全性を高めるため、環境変数の型定義を作成することもおすすめです。

typescript// src/env.d.ts
/// <reference types="astro/client" />

interface ImportMetaEnv {
  readonly PUBLIC_API_URL: string;
  readonly PUBLIC_GA_ID: string;
  readonly PUBLIC_APP_ENV:
    | 'development'
    | 'staging'
    | 'production';
  readonly ENABLE_DEBUG: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

この型定義により、IDE での補完が効くようになり、タイプミスを防げますよ。

CI/CD での環境切り替え

GitHub Actions を使った CI/CD パイプラインで環境を切り替える例を見てみましょう。

まず、環境ごとにワークフローファイルを作成するか、1 つのワークフローで環境を分岐させます。以下は、ブランチごとに環境を切り替える例です。

yaml# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches:
      - develop
      - staging
      - main

環境を判定するステップを追加します。ブランチ名から自動的に環境を決定する仕組みにしましょう。

yamljobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Determine environment
        id: env
        run: |
          if [ "${{ github.ref }}" = "refs/heads/main" ]; then
            echo "env=production" >> $GITHUB_OUTPUT
          elif [ "${{ github.ref }}" = "refs/heads/staging" ]; then
            echo "env=staging" >> $GITHUB_OUTPUT
          else
            echo "env=development" >> $GITHUB_OUTPUT
          fi

Node.js のセットアップとパッケージのインストールを行います。Yarn を使用する場合は、キャッシュを有効にすると高速化できますよ。

yaml- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'yarn'

- name: Install dependencies
  run: yarn install --frozen-lockfile

環境に応じたビルドコマンドを実行します。先ほど定義した package.json のスクリプトを使用しましょう。

yaml- name: Build
  run: |
    if [ "${{ steps.env.outputs.env }}" = "production" ]; then
      yarn build:prd
    elif [ "${{ steps.env.outputs.env }}" = "staging" ]; then
      yarn build:stg
    else
      yarn build:dev
    fi
  env:
    PUBLIC_API_URL: ${{ secrets[format('API_URL_{0}', steps.env.outputs.env)] }}
    PUBLIC_GA_ID: ${{ secrets[format('GA_ID_{0}', steps.env.outputs.env)] }}

GitHub Secrets には、環境ごとの秘密情報を API_URL_productionAPI_URL_staging のような命名規則で登録しておきます。

以下の図は、CI/CD パイプラインでの環境切り替えフローを示しています。

mermaidflowchart TD
  push["Git Push"] -->|"ブランチ判定"| branch{Branch?}

  branch -->|"develop"| devEnv["開発環境<br/>PUBLIC_APP_ENV=development"]
  branch -->|"staging"| stgEnv["ステージング環境<br/>PUBLIC_APP_ENV=staging"]
  branch -->|"main"| prdEnv["本番環境<br/>PUBLIC_APP_ENV=production"]

  devEnv --> devBuild["yarn build:dev"]
  stgEnv --> stgBuild["yarn build:stg"]
  prdEnv --> prdBuild["yarn build:prd"]

  devBuild --> devDeploy["Deploy to Dev Server"]
  stgBuild --> stgDeploy["Deploy to Staging Server"]
  prdBuild --> prdDeploy["Deploy to Production Server"]

  devDeploy -.->|"確認"| devTest["開発環境での動作確認"]
  stgDeploy -.->|"確認"| stgTest["ステージング環境でのテスト"]
  prdDeploy -.->|"確認"| prdTest["本番環境での監視"]

このフローにより、ブランチへの push 操作だけで自動的に適切な環境へのデプロイが実行されます。

環境情報の表示コンポーネント

開発中に現在の環境を視覚的に確認できるコンポーネントを作成すると便利です。

環境情報を表示する専用コンポーネントを作成しましょう。本番環境では非表示にし、開発・ステージング環境でのみ表示されるようにします。

typescript---
// src/components/EnvIndicator.astro
const appEnv = import.meta.env.PUBLIC_APP_ENV;
const isProduction = appEnv === 'production';

// 本番環境では何も表示しない
if (isProduction) {
  return null;
}

const envColors = {
  development: '#10b981',
  staging: '#f59e0b',
} as const;

const envColor = envColors[appEnv as keyof typeof envColors];
---

スタイルを含めた表示部分を実装します。画面右下に固定表示されるバッジを作成しましょうね。

astro<div class="env-indicator" style={`background-color: ${envColor}`}>
  <span class="env-label">{appEnv.toUpperCase()}</span>
  <span class="env-api">{import.meta.env.PUBLIC_API_URL}</span>
</div>

<style>
  .env-indicator {
    position: fixed;
    bottom: 1rem;
    right: 1rem;
    padding: 0.5rem 1rem;
    border-radius: 0.5rem;
    color: white;
    font-size: 0.875rem;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  }

  .env-label {
    font-weight: bold;
  }

  .env-api {
    font-size: 0.75rem;
    opacity: 0.9;
  }
</style>

このコンポーネントをレイアウトファイルに追加すると、全ページで環境情報が表示されるようになります。

astro---
// src/layouts/Layout.astro
import EnvIndicator from '../components/EnvIndicator.astro';
---

<!DOCTYPE html>
<html lang="ja">
  <head>
    <!-- ... -->
  </head>
  <body>
    <slot />
    <EnvIndicator />
  </body>
</html>

開発中にこのバッジが表示されることで、現在どの環境で作業しているかが一目瞭然になりますよ。

エラーハンドリングと環境別デバッグ

環境に応じてエラー表示の詳細度を変更する実装も重要です。

エラーハンドリング用のユーティリティ関数を作成しましょう。開発環境では詳細なスタックトレースを表示し、本番環境ではユーザーフレンドリーなメッセージのみを表示します。

typescript// src/utils/errorHandler.ts
const isDevelopment =
  import.meta.env.PUBLIC_APP_ENV === 'development';
const enableDebug = import.meta.env.ENABLE_DEBUG === 'true';

export function handleError(
  error: Error,
  context?: string
): void {
  if (isDevelopment || enableDebug) {
    console.error(
      `[Error] ${context || 'Unknown context'}:`,
      error
    );
    console.error('Stack trace:', error.stack);
  } else {
    // 本番環境では最小限のログのみ
    console.error(
      'An error occurred. Please try again later.'
    );
  }
}

API 呼び出しのエラーハンドリングに適用してみましょう。環境に応じて適切なエラー情報が出力されます。

typescript// src/utils/api.ts
import { handleError } from './errorHandler';

export async function fetchApi<T>(
  endpoint: string
): Promise<T | null> {
  const baseUrl = import.meta.env.PUBLIC_API_URL;

  try {
    const response = await fetch(`${baseUrl}${endpoint}`);

    if (!response.ok) {
      throw new Error(
        `HTTP Error: ${response.status} ${response.statusText}`
      );
    }

    return await response.json();
  } catch (error) {
    handleError(error as Error, `API call to ${endpoint}`);
    return null;
  }
}

環境別のデバッグログ出力関数も用意しておくと便利です。

typescript// src/utils/logger.ts
const isDevelopment =
  import.meta.env.PUBLIC_APP_ENV === 'development';

export const logger = {
  debug: (...args: any[]) => {
    if (isDevelopment) {
      console.log('[DEBUG]', ...args);
    }
  },
  info: (...args: any[]) => {
    console.log('[INFO]', ...args);
  },
  error: (...args: any[]) => {
    console.error('[ERROR]', ...args);
  },
};

これらのユーティリティを使うことで、環境に応じた適切なデバッグとエラー処理が実現できますね。

まとめ

Astro プロジェクトにおける複数環境の切り替え設定と運用フローについて解説しました。

環境変数ファイルを分離し、package.json でビルドコマンドを整備することで、開発・ステージング・本番環境を明確に管理できるようになります。astro.config.mjs での環境別設定により、環境ごとに最適化されたビルド結果が得られるのです。

CI/CD パイプラインと連携させることで、ブランチへの push だけで自動的に適切な環境へデプロイされる仕組みも構築できました。環境情報表示コンポーネントやエラーハンドリングの工夫により、開発効率とデバッグのしやすさも向上しますね。

この設定を導入することで、チーム全体で統一された環境管理が可能になり、ヒューマンエラーのリスクも大幅に削減できるでしょう。環境ごとの設定値は .env ファイルで集中管理されるため、変更時の影響範囲も明確になります。

Astro の静的サイト生成という特性を活かしながら、複数環境を適切に管理する仕組みを構築できました。この運用フローを基盤として、より安全で効率的な開発体制を整えていってくださいね。

関連リンク