## [Runtime Environment Variables](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#runtime-environment-variables) 有兩種使用env的方式,分別是`build time`和`runtime` `build time`是在`next build`時就決定好env(專案根目錄.env),但這方法會造成同樣的source code多個image版本 `runtime` env則可以build好image,後續再deploy時在決定env,可以避免因為env不同而build image,減少系統資源的消耗,對維護來說是比較好的選擇 如果使用的是`build time` env,要讓env可以在瀏覽器被存取,env必須有prefix `NEXT_PUBLIC_`,否則只能在server端被存取,在`next build`時,這些env會被轉成Javascript,但是如果使用的router不同,會不同的結果 要存取`runtime` env,Page-Router必須使用`GetServerSideProps`,App-Router則可以使用`unstable_noStore();`,官方建議使用App-Router,以下為每個組合的不同結果 ### Page-Router - `build time` env 只能讀取到有Prefix `NEXT_PUBLIC_`的env,無Prefix `NEXT_PUBLIC_`會閃一下後跳Error - `build time` env with `GetServerSideProps` 不論是否有Prefix `NEXT_PUBLIC_`都會在瀏覽器上顯示 - `build time` env conbine `runtime` env 無法讀取到`runtime` env,`runtime` env 會閃一下後跳Error - `build time` env conbine `runtime` env with `GetServerSideProps` **有Prefix** `NEXT_PUBLIC_`會被`runtime` env取代 - `runtime` env 無法顯示任何env - `runtime` env with `GetServerSideProps` 不論是否有Prefix `NEXT_PUBLIC_`都會在瀏覽器上顯示 ### App-Router - `build time` env 不論是否有Prefix `NEXT_PUBLIC_`都會在瀏覽器上顯示 - `build time` env with `unstable_noStore();` 不論是否有Prefix `NEXT_PUBLIC_`都會在瀏覽器上顯示 - `build time` env conbine `runtime` env 無法讀取到`runtime` env - `build time` env conbine `runtime` env with `unstable_noStore();` **無Prefix** `NEXT_PUBLIC_`會被`runtime` env取代 - `runtime` env 無法顯示任何env - `runtime` env with `unstable_noStore();` 不論是否有Prefix `NEXT_PUBLIC_`都會在瀏覽器上顯示 ### 測試指令 ``` docker compose up --build ``` - next.config.mjs ```typescript= /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone" }; export default nextConfig; ``` - Dockerfile ```dockerfile= FROM node:20-alpine AS base # Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED 1 RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi # Production image, copy all the files and run next FROM base AS runner WORKDIR /app ENV NODE_ENV production # Uncomment the following line in case you want to disable telemetry during runtime. # ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public # Set the correct permission for prerender cache RUN mkdir .next RUN chown nextjs:nodejs .next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT 3000 # server.js is created by next build from the standalone output # https://nextjs.org/docs/pages/api-reference/next-config-js/output CMD HOSTNAME="0.0.0.0" node server.js ``` - docker-compose.yaml ```yaml= services: frontend: build: context: . dockerfile: Dockerfile container_name: nextjspage ports: - "3000:3000" env_file: - env/staging.env ``` - index.tsx(Page-Router) ```typescript= import type { InferGetServerSidePropsType, GetServerSideProps } from "next"; type RuntimeEnv = { backendUrl: string; apiPath: string; apiVersion: string; n_backendUrl: string; n_apiPath: string; n_apiVersion: string; }; export const getServerSideProps = (async () => { // Fetch data from external API const runtimeEnv: RuntimeEnv = { backendUrl: process.env.NEXT_PUBLIC_BACKEND_URL || "", apiPath: process.env.NEXT_PUBLIC_API_PATH || "", apiVersion: process.env.NEXT_PUBLIC_API_VERSION || "", n_backendUrl: process.env.BACKEND_URL || "", n_apiPath: process.env.API_PATH || "", n_apiVersion: process.env.API_VERSION || "", }; // Pass data to the page via props return { props: { runtimeEnv } }; }) satisfies GetServerSideProps<{ runtimeEnv: RuntimeEnv }>; export default function Page({ runtimeEnv }: InferGetServerSidePropsType<typeof getServerSideProps>) { console.log(`===========`); console.log(`NODE_ENV: ${process.env.NODE_ENV}`); console.log(`BACKEND_URL: ${process.env.BACKEND_URL}`); console.log(`API_PATH: ${process.env.API_PATH}`); console.log(`API_VERSION: ${process.env.API_VERSION}`); console.log(`NEXT_PUBLIC_BACKEND_URL: ${process.env.NEXT_PUBLIC_BACKEND_URL}`); console.log(`NEXT_PUBLIC_API_PATH: ${process.env.NEXT_PUBLIC_API_PATH}`); console.log(`NEXT_PUBLIC_API_VERSION: ${process.env.NEXT_PUBLIC_API_VERSION}`); console.log(`===========`); return ( <main className="flex min-h-screen flex-col items-center justify-between p-24 bg-green-200"> <p>BACKEND_URL: {runtimeEnv.backendUrl}</p> <p>API_PATH: {runtimeEnv.apiPath}</p> <p>API_VERSION: {runtimeEnv.apiVersion}</p> <p>NEXT_PUBLIC_BACKEND_URL: {runtimeEnv.n_backendUrl}</p> <p>NEXT_PUBLIC_API_PATH: {runtimeEnv.n_apiPath}</p> <p>NEXT_PUBLIC_API_VERSION: {runtimeEnv.n_apiVersion}</p> </main> ); } ``` - page.tsx(App-router) ```typescript= import { unstable_noStore } from "next/cache"; export default function Home() { unstable_noStore(); console.log(`===========`); console.log(`NODE_ENV: ${process.env.NODE_ENV}`); console.log(`BACKEND_URL: ${process.env.BACKEND_URL}`); console.log(`API_PATH: ${process.env.API_PATH}`); console.log(`API_VERSION: ${process.env.API_VERSION}`); console.log(`NEXT_PUBLIC_BACKEND_URL: ${process.env.NEXT_PUBLIC_BACKEND_URL}`); console.log(`NEXT_PUBLIC_API_PATH: ${process.env.NEXT_PUBLIC_API_PATH}`); console.log(`NEXT_PUBLIC_API_VERSION: ${process.env.NEXT_PUBLIC_API_VERSION}`); console.log(`===========`); return ( <main className="flex min-h-screen flex-col items-center justify-between p-24 bg-green-200"> <p>BACKEND_URL: {process.env.BACKEND_URL}</p> <p>API_PATH: {process.env.API_PATH}</p> <p>API_VERSION: {process.env.API_VERSION}</p> <p>NEXT_PUBLIC_BACKEND_URL: {process.env.NEXT_PUBLIC_BACKEND_URL}</p> <p>NEXT_PUBLIC_API_PATH: {process.env.NEXT_PUBLIC_API_PATH}</p> <p>NEXT_PUBLIC_API_VERSION: {process.env.NEXT_PUBLIC_API_VERSION}</p> </main> ); } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up