Skip to content

Commit

Permalink
set up helper for environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
ndinata committed Apr 16, 2024
1 parent ad8ff4e commit a38a236
Show file tree
Hide file tree
Showing 8 changed files with 628 additions and 16 deletions.
6 changes: 6 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# NOTE: this file is checked into Git to help devs get their local environment
# working quickly. Keep this in mind when deciding what values to use here.

# This file is to be kept in sync with the schema in `build-env.ts`.

GREETING="Dev"
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ yarn-error.*
.DS_Store
*.pem

# local env files
.env*.local
# env files (except for .env.development)
.env*
!.env.development

# typescript
*.tsbuildinfo
Expand Down
8 changes: 8 additions & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// This is to allow importing other TS files.
import "@swc-node/register";

import { ConfigContext, ExpoConfig } from "expo/config";

import { Env } from "./build-env";

export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: "navy",
Expand Down Expand Up @@ -34,4 +39,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
experiments: {
typedRoutes: true,
},
extra: {
...Env,
},
});
104 changes: 104 additions & 0 deletions build-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* This file is where you define the schema for environment variables in env files.
* NOTE: DO NOT import this file in the client (`src/`), use `src/env.ts` instead.
*
* To add/modify an env var:
* 1. Add/modify the var name along with its expected type in `ENV_SCHEMA` below
* 2. Add/modify the env var in all env files
*
* To read an env var:
* - if using it in `app.config.ts` or other "config" files, import `Env` below
* - if using it in the client (`src/`), import `Env` from `src/env.ts` instead
*
* To choose which env file to load vars from, set the `APP_ENV` env var to either
* one of these values when running Expo CLI commands:
* - "development", corresponding to `.env.development` (default if unspecified)
* - "preview", corresponding to `.env.preview`
* - "production", corresponding to `.env.production`
* Check out `package.json` for scripts that help do this for you.
*
* NO SECRETS: NO encryption/obfuscation is applied to env vars used in the app.
* They're all in plain text and should be assumed to be accessible to app users.
* Keep this in mind as you design your API integrations.
*/

import { existsSync } from "fs";
import path from "path";

import dotenv from "dotenv";
import { z } from "zod";

enum Environment {
Dev = "development",
Preview = "preview",
Prod = "production",
}

/** If `APP_ENV` is unspecified, the default is "development". */
const _APP_ENV = (process.env["APP_ENV"] as Environment) ?? Environment.Dev;

/**
* --------------------------------------------------------------------------
* ENV_SCHEMA
*
* If you're simply adding/changing env vars, this schema is the only thing in
* this file that you'd need to change (don't forget to actually add/change the
* env var in the env files!).
* ------------------------------------------------------------------------ */

const ENV_SCHEMA = z.object({
/** The environment the app is run in. ("development" | "preview" | "production") */
APP_ENV: z.nativeEnum(Environment).default(_APP_ENV),

GREETING: z.string(),
});

/**
* --------------------------------------------------------------------------
* Utility functions
* ------------------------------------------------------------------------ */

/**
* Loads environment variables from the env file corresponding to `APP_ENV` into
* `process.env`.
*
* The target env file should have a name of `.env.${APP_ENV}` and be placed in
* the root directory.
*
* You can skip calling this function if you have other means of loading env vars
* into `process.env` (e.g. via your CI provider secrets).
*/
function _loadEnvVars() {
const envfilePath = path.resolve(__dirname, `.env.${_APP_ENV}`);
if (!existsSync(envfilePath)) {
throw new Error(
`Using the "${_APP_ENV}" environment but ".env.${_APP_ENV}" doesn't exist. Please create and populate it according to the schema in "build-env.ts".`,
);
}

dotenv.config({ path: envfilePath });
}

/**
* Returns parsed environment variables from `process.env`.
*/
function _parseProcessEnv() {
const parsed = ENV_SCHEMA.safeParse(process.env);
if (!parsed.success) {
throw new Error(
`Invalid environment variables in ".env.${_APP_ENV}":\n${JSON.stringify(parsed.error.flatten().fieldErrors)}\nRefer to the schema in "build-env.ts" to load them correctly.`,
);
}

return parsed.data;
}

_loadEnvVars();

/**
* NOTE: this `Env` is not safe to import _directly_ in the client (`src/`).
* To use env vars in the client, import from `src/env.ts` instead.
*/
export const Env = _parseProcessEnv();

export type EnvType = typeof Env;
Loading

0 comments on commit a38a236

Please sign in to comment.