Skip to content
/ env-plus Public

Toolkit for managing type-safe environment variables with the feature flags built-in support. Heavily influenced by t3-env.

License

Notifications You must be signed in to change notification settings

tonik/env-plus

Repository files navigation

Env+

Toolkit for managing type-safe environment variables with the feature flags built-in support. Heavily influenced by t3-env.

Features

  • Full type safety
  • API compatible with @t3-oss/env-core
  • Built-in feature flags support
  • Full schema transformation support

Installation

pnpm add @tonik/env-plus zod

How to define env schema

// src/env.mjs
// @ts-check

import { createEnv } from "@tonik/env-plus";
import { z } from "zod";

export const env = createEnv({
  /**
   * Example settings for Next.js
   */
  isServer: typeof window === "undefined"
  clientPrefix: "NEXT_PUBLIC_"
  /**
   * All envs here will be avaliable to read when isServer is true
   */
  server: {
    DATABASE_URL: z.string().url(),
    SERVER_ENV: z.string(),
    /**
     * You can define feature flag schema here.
     * You can use any input here as long it returns boolean.
     */
    GOOGLE_AUTH_ENABLED: z
      .enum(["true", "false"])
      .default("false")
      .transform((e) => e === "true"),

    /**
     * Here are variables that will become required when flag is set to "true".
     * For now define them as optionals.
     */
    GOOGLE_CLIENT_ID: z.string().min(1).optional(),
    GOOGLE_CLIENT_SECRET: z.string().min(1).optional()
  },
  /**
   * All envs here will be avaliable to read when isServer is false.
   *
   * You can set here anything prefixed with value in `clientPrefix`
   */
  client: {
    NEXT_PUBLIC_PLASMIC_PUBLIC_KEY: z.string().min(1),
    NEXT_PUBLIC_CLIENT_ENV: z.string().min(1)
  },
  /**
   * Custom env transformation. Usefull when you want to apply custom
   * logic based on shared, server, or client schema.
   *
   * It can define new keys that will be available for featureFlags definitions.
   */
  transform: (env) => {
    return {
        CUSTOM_FLAG: true,
        SOME_DYNAMIC_ENV: `${env.NEXT_PUBLIC_CLIENT_ENV}${env.SERVER_ENV}`
    }
  },
  /**
   * Define relations between envs here.
   * 1st level - boolean envs.
   * 2nd level - all envs that will drop `optional` when 1st level env is set to true.
   *
   * Only `true` as a value allows droping `optional` schema right now. Negation isn't implemented.
   */
  featureFlags: {
    CUSTOM_FLAG: {
        SOME_DYNAMIC_ENV: true
    },
    GOOGLE_AUTH_ENABLED: {
        GOOGLE_CLIENT_ID: true,
        GOOGLE_CLIENT_SECRET: true
    }
  },
  runtimeEnv: {
    DATABASE_URL: process.env.DATABASE_URL,
    SERVER_ENV: process.env.SERVER_ENV,
    NEXT_PUBLIC_PLASMIC_PUBLIC_KEY:
      process.env.NEXT_PUBLIC_PLASMIC_PUBLIC_KEY,
    NEXT_PUBLIC_CLIENT_ENV: process.env.NEXT_PUBLIC_CLIENT_ENV,
    GOOGLE_AUTH_ENABLED: process.env.GOOGLE_AUTH_ENABLED,
    GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
    GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET
  },
});

How to use env in the application

import { env } from "./src/env.mjs";

// all defined envs are typed and runtime avaliable
env.DATABASE_URL; // type string
env.CUSTOM_FLAG; // type boolean
env.SOME_DYNAMIC_VALUE; // type string

env.GOOGLE_CLIENT_ID; // type string | undefined
env.GOOGLE_CLIENT_SECRET; // type string | undefined

if (env.GOOGLE_AUTH_ENABLED) {
  env.GOOGLE_CLIENT_ID; // type string (undefined dropped)
  env.GOOGLE_CLIENT_SECRET; // type string (undefined dropped)
}

Enable type-checking by including env.mjs file in your tsconfig.json and turning on allowJs flag

// tsconfig.json
{
  "compilerOptions": {
    "allowJs": true
  },
  "include": ["src/env.mjs"]
}

License

Licensed under the MIT license.

About

Toolkit for managing type-safe environment variables with the feature flags built-in support. Heavily influenced by t3-env.

Resources

License

Stars

Watchers

Forks

Packages