LoginSignup
0
0

More than 1 year has passed since last update.

Deno Deploy + Fresh で production 環境か preview 環境かを判定する

Last updated at Posted at 2022-12-04

結論

できない。

やり方

完璧にはできないけど、URL でおおむね正確に判定することはできる。Deno Deploy が production 用には短い URL を発行し、Preview 用には長い URL を発行してくれることを利用する。

判定するコード

_middleware で、着信したリクエストの URL がどの環境に当たるかを表現で検査して、ctx.state.target にセットする。
判定方法は、何でもいいけど、ここでは泥臭い正規を使う。

routes/_middleware.ts
import { MiddlewareHandlerContext } from "$fresh/server.ts";

type Target = "local" | "preview" | "production" | undefined;

const {
  TARGET_PRODUCTION_REGEX = "^my-great-project[.]deno[.]dev$",
  TARGET_PREVIEW_REGEX = "^my-great-project-(.+)[.]deno[.]dev$",
  TARGET_LOCAL_REGEX = "^localhost$",
} = Deno.env.toObject();

function targetEnv(req): Target {
  const { hostname } = new URL(req.url);
  if (hostname.match(TARGET_PRODUCTION_REGEX)) {
    return "production";
  } else if (hostname.match(TARGET_PREVIEW_REGEX)) {
    return "preview";
  } else if (hostname.match(TARGET_LOCAL_REGEX)) {
    return "local";
  } else {
    console.log(`No target matched for ${hostname}`);
    return undefined;
  }
}

interface State {
  target: Target;
}

export async function handler(
  req: Request,
  ctx: MiddlewareHandlerContext<State>,
) {
  ctx.state.target = targetEnv(req);
  const resp = await ctx.next();
  return resp;
}

Deno.env に何も指定がなければ、デフォルトの正規表現を使う。環境変数を設定していたら、デフォルトは使わずに、そこから読み込む。

判定結果を使ってみる

index.tsx で、ctx.state.target を読み込む。handler 内での条件分岐に使うだけなら、そのまま if や switch で分岐して、最後に ctx.render() を忘れずに返す。

routes/index.tsx
import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = {
  GET(_req, ctx) {
    const { target } = ctx.state;
    switch (target) {
        case "production":
            ...
    }
    return ctx.render();
  },
};

ページに渡す必要があるならば、Data を定義して、ctx.render() でページに渡す。

routes/index.tsx
import { Handlers } from "$fresh/server.ts";

interface Data {
  target: string;
}

export const handler: Handlers = {
  GET(_req, ctx) {
    const { target } = ctx.state;
    const data: Data = { target };
    return ctx.render(data);
  },
};

export default function Page({ data }: PageProps<Data>) {
  const { target } = data;
  return (
    <>
      <p>Welcome to `fresh`.</p>
      <div>env target: {target}</div>
    </>
  );
}

期待される出力

ローカル:

<p>Welcome to `fresh`.</p>
<div>env target: local</div>

プレビュー用の長い URL:

<p>Welcome to `fresh`.</p>
<div>env target: preview</div>

本番用の短い URL:

<p>Welcome to `fresh`.</p>
<div>env target: production</div>

せいこう

ダメなところ

Deno Deploy では、本番環境用に短い URL に加えて、長い URL も発行される。
わざわざそれにアクセスすると、

<p>Welcome to `fresh`.</p>
<div>env target: preview</div>

こうなる。

リクエストごとにどちらの環境になるのか予測不能なので、起動時にどちらなのか固定化することはできない。判定は、リクエストごとに必要になる。

キャッシュ的な動きをする変数があるなら、誤動作しないように注意する必要がある。本番環境なのにプレビュー用のAPI呼び出しを取りに行ったり、プレビューなのに本番用DBから取ったデータを返したり、というのがないように。

わざわざ長い URL にアクセスすることは稀と考えて、だいたい OK。

宣伝

宣伝: Fresh のドキュメントを https://fresh-ja.deno.dev/ で勝手に日本語化しています。協力者を募集中。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0