結論
できない。
やり方
完璧にはできないけど、URL でおおむね正確に判定することはできる。Deno Deploy が production 用には短い URL を発行し、Preview 用には長い URL を発行してくれることを利用する。
判定するコード
_middleware で、着信したリクエストの URL がどの環境に当たるかを表現で検査して、ctx.state.target
にセットする。
判定方法は、何でもいいけど、ここでは泥臭い正規を使う。
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()
を忘れずに返す。
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()
でページに渡す。
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/ で勝手に日本語化しています。協力者を募集中。