~app/env.ts
.ts
TypeScript
(application/typescript)
// std
import { existsSync } from "node:fs";
import { join, resolve } from "node:path";
// 3rd-party
import dotEnvFlow from "dotenv-flow";

import { Env as NodeEnv } from "@ethicdevs/react-monolith";

const NODE_ENV = process.env.NODE_ENV;
const ENV_FILES_DIR = resolve(join(__dirname, ".."));

const baseOpts = {
  node_env: NODE_ENV,
};

const files = dotEnvFlow.listDotenvFiles(ENV_FILES_DIR, baseOpts);

function getLoadableFilesDisplay(): string {
  return files.filter((path) => existsSync(path) === true).join("\n  * ");
}

if (files.length > 0) {
  console.log(`[env] Loading environment variables from files:
  * ${getLoadableFilesDisplay()}`);
} else {
  console.log(
    `[env] Found no file to load environment variables from, using system environment.`
  );
}

const res = dotEnvFlow.config({
  ...baseOpts,
  encoding: "utf8",
  default_node_env: "development",
  purge_dotenv: true, // Avoid dependencies 'dotenv' to take priority
  silent: true,
});

if (res.error) {
  throw res.error;
}

const isValidNodeEnv = (env?: string | null): env is NodeEnv => {
  if (env == null) {
    return false;
  }
  if (["development", "test", "production"].includes(env)) {
    return true;
  }
  return false;
};

const getNodeEnv = (
  env?: string | null,
  defaultEnv: "production" = "production"
): NodeEnv => {
  if (isValidNodeEnv(env)) {
    return env;
  }
  return defaultEnv;
};

const getHostname = (hostname?: string | null): string => {
  if (hostname == null || hostname === "fake") {
    return "localhost";
  }
  return String(hostname);
};

const getPort = (port?: string | null): number => {
  if (port == null || port === "fake") {
    return 4100;
  }
  return Number(port);
};

const getCookieName = (cookieName?: string | null): string => {
  if (cookieName == null || cookieName === "fake") {
    throw new Error("[env] COOKIE_NAME is missing.");
  }
  return String(cookieName);
};

const getDatabaseUrl = (databaseUrl?: string | null): string => {
  if (databaseUrl == null || databaseUrl === "fake") {
    throw new Error("[env] DATABASE_URL is missing.");
  }
  return String(databaseUrl);
};

const getDeploymentDomain = (deploymentDomain?: string | null): string => {
  if (deploymentDomain == null || deploymentDomain === "fake") {
    throw new Error("[env] DEPLOYMENT_DOMAIN is missing.");
  }
  return String(deploymentDomain);
};

const getDeploymentScheme = (
  deploymentScheme?: string | null
): "http" | "https" => {
  if (deploymentScheme == null || deploymentScheme === "fake") {
    throw new Error("[env] DEPLOYMENT_SCHEME is missing.");
  }
  const validSchemes = ["http", "https"];
  if (validSchemes.includes(deploymentScheme) === false) {
    throw new Error(
      `[env] DEPLOYMENT_SCHEME value is invalid. Must be one of: "${validSchemes.join(
        '", "'
      )}". Received: "${deploymentScheme}".`
    );
  }
  return String(deploymentScheme) as "http" | "https";
};

const getCookieSecret = (cookieSecret?: string | null): string => {
  if (cookieSecret == null || cookieSecret === "fake") {
    throw new Error("[env] COOKIE_SECRET is missing.");
  }
  return String(cookieSecret);
};

const getGitRepositoriesRoot = (
  gitRepositoriesRoot?: string | null
): string => {
  if (gitRepositoriesRoot == null || gitRepositoriesRoot === "fake") {
    throw new Error("[env] GIT_REPOSITORIES_ROOT is missing.");
  }
  return String(gitRepositoriesRoot);
};

interface IEnv {
  NODE_ENV: NodeEnv;
  PORT: number;
  HOST: string;
  COOKIE_NAME: string;
  COOKIE_SECRET: string;
  DATABASE_URL: string;
  DEPLOYMENT_DOMAIN: string;
  DEPLOYMENT_SCHEME: "http" | "https";
  GIT_REPOSITORIES_ROOT: string;
}

export const Env: IEnv = {
  NODE_ENV: getNodeEnv(process.env.NODE_ENV),
  PORT: getPort(process.env.PORT),
  HOST: getHostname(process.env.HOST),
  // ---
  COOKIE_NAME: getCookieName(process.env.COOKIE_NAME),
  COOKIE_SECRET: getCookieSecret(process.env.COOKIE_SECRET),
  // --
  DATABASE_URL: getDatabaseUrl(process.env.DATABASE_URL),
  // ---
  DEPLOYMENT_DOMAIN: getDeploymentDomain(process.env.DEPLOYMENT_DOMAIN),
  DEPLOYMENT_SCHEME: getDeploymentScheme(process.env.DEPLOYMENT_SCHEME),
  // ---
  GIT_REPOSITORIES_ROOT: getGitRepositoriesRoot(
    process.env.GIT_REPOSITORIES_ROOT
  ),
};

GitFOSS