Skip to content

Build once, deploy anywhere. Framework agnostic environment variables generator and validator

License

Notifications You must be signed in to change notification settings

runtime-env/runtime-env

Repository files navigation

runtime-env

The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code. - The Twelve-Factor App

Table of Content

Installation

$ npm i -D @runtime-env/cli

Get Started

  1. Source code

    index.html:

    <!doctype html>
    <html>
      <head>
        <title><%= runtimeEnv.TITLE %></title>
      </head>
      <body>
        <script src="/runtime-env.js"></script>
        <script src="/index.js"></script>
      </body>
    </html>

    index.js:

    console.log(runtimeEnv.TITLE);

    .runtimeenvschema.json:

    {
      "type": "object",
      "properties": {
        "TITLE": {
          "type": "string"
        }
      },
      "required": ["TITLE"]
    }
  2. Run commands

    user environment:

    $ export TITLE="Hello Runtime Env!"

    Run npx -p @runtime-env/cli runtime-env interpolate --input-file-path index.html --output-file-path index.html to interpolate the template in the html file:

    <!doctype html>
    <html>
      <head>
        <title>Hello Runtime Env!</title>
      </head>
      <body>
        <script src="/runtime-env.js"></script>
        <script src="/index.js"></script>
      </body>
    </html>

    Run npx -p @runtime-env/cli runtime-env gen-js --output-file-path runtime-env.js to generate a JavaScript file which contains environment variables:

    runtime-env.js:

    globalThis.runtimeEnv = { TITLE: "Hello Runtime Env!" };

    Run npx -p @runtime-env/cli runtime-env gen-ts --output-file-path runtime-env.d.ts to generate a TypeScript file which contains corresponding types of environment variables:

    runtime-env.d.ts:

    declare const runtimeEnv: { readonly TITLE: string };
  3. The final result

    index.html (modified):

    <!doctype html>
    <html>
      <head>
        <title>Hello Runtime Env!</title>
      </head>
      <body>
        <script src="/runtime-env.js"></script>
        <script src="/index.js"></script>
      </body>
    </html>

    index.js:

    console.log(runtimeEnv.TITLE);

    runtime-env.js (generated):

    globalThis.runtimeEnv = { TITLE: "Hello Runtime Env!" };

    runtime-env.d.ts (generated):

    declare const runtimeEnv: { TITLE: string };

    .runtimeenvschema.json:

    {
      "type": "object",
      "properties": {
        "TITLE": {
          "type": "string"
        }
      },
      "required": ["TITLE"]
    }

Setup

  1. Create a JSON-schema file:

    .runtimeenvschema.json:

    {
      "type": "object",
      "properties": {
        "TAG_ID": {
          "type": "string"
        },
        "FIREBASE_CONFIG": {
          "type": "object",
          "properties": {
            "apiKey": {
              "type": "string"
            },
            "authDomain": {
              "type": "string"
            }
          },
          "required": ["apiKey", "authDomain"]
        }
      },
      "required": ["TAG_ID", "FIREBASE_CONFIG"]
    }

    You could use other file name by running npx -p @runtime-env/cli runtime-env --env-schema-file-path ....

    Runtime-env uses Ajv to parse your JSON-schema.

  2. Configure your package.json, Dockerfile, etc. to generate, and interpolate files:

    • To generate a JavaScript file which contains the environment variables, you will execute runtime-env gen-js:

      $ npx -p @runtime-env/cli runtime-env gen-js --output-file-path runtime-env.js
      // runtime-env.js
      globalThis.runtimeEnv = {
        TAG_ID: "G-ABCD12345",
        FIREBASE_CONFIG: {
          apiKey: "AIzaSk2CngkeAyDJr2BhzSprQXp8PsZs3VFJFMA",
          authDomain: "example.firebaseapp.com",
        },
      };

      And make sure to import the generated file before importing the entry point:

      <!-- index.html -->
      <script src="/runtime-env.js"></script>
      <script src="/index.js"></script>
    • If you are using TypeScript, you can also generate a declaration file by running runtime-env gen-ts:

      $ npx -p @runtime-env/cli runtime-env gen-ts --output-file-path runtime-env.d.ts
      // runtime-env.d.ts
      declare const runtimeEnv: {
        readonly TAG_ID: string;
        readonly FIREBASE_CONFIG: {
          readonly apiKey: string;
          readonly authDomain: string;
        };
      };
    • For index.html and other non-JavaScript files, if needed, you can run runtime-env interpolate:

      $ npx -p @runtime-env/cli runtime-env interpolate --input-file-path index.html --output-file-path index.html
      <script
        async
        src="https://www.googletagmanager.com/gtag/js?id=G-ABCD12345"
      ></script>
  3. Further setups:

    • You NEED to set up your web server to stop runtime-env.js to be cached by browser or CDNs.

    • To use runtime-env on systems that don't have Node.js installed, you'll need to pack runtime-env CLI into a single runnable file. Here's how you can do it:

      • Make a single runnable app using NodeJS's Single Executable Applications feature (experimental).

      • Pack runtime-env into a runnable file using pkg:

        $ pkg ./node_modules/@runtime-env/cli/bin/runtime-env.js --target node18-alpine-x64 --output runtime-env
    • If you're making a PWA (Progressive Web App), you HAVE TO set up your ServiceWorker to choose the right way to cache runtime-env.js.

Commands

  • $ npx -p @runtime-env/cli runtime-env --help

    Usage: runtime-env [options] [command]
    
    Options:
      -V, --version                                output the version number
      --global-variable-name <globalVariableName>  specify the global variable name (default: "runtimeEnv")
      --env-schema-file-path <envSchemaFilePath>   specify the json schema file to be loaded (default: ".runtimeenvschema.json")
      -h, --help                                   display help for command
    
    Commands:
      interpolate [options]                        perform template interpolation by substituting environment variables
      gen-js [options]                             generate a JavaScript file that includes environment variables within an object, making them accessible through the globalThis property
      gen-ts [options]                             generate a TypeScript file that provides the corresponding type definitions for the JavaScript file generated by the gen-js command
      help [command]                               display help for command
    
  • $ npx -p @runtime-env/cli runtime-env interpolate --help

    Usage: runtime-env interpolate [options]
    
    perform template interpolation by substituting environment variables
    
    Options:
      --input-file-path <inputFilePath>    specify the input file to be loaded instead of being read from stdin
      --output-file-path <outputFilePath>  specify the output file to be written instead of being piped to stdout
      -h, --help                           display help for command
    
  • $ npx -p @runtime-env/cli runtime-env gen-js --help

    Usage: runtime-env gen-js [options]
    
    generate a JavaScript file that includes environment variables within an object, making them accessible through the globalThis property
    
    Options:
      --output-file-path <outputFilePath>  specify the output file to be written instead of being piped to stdout
      -h, --help                           display help for command
    
  • $ npx -p @runtime-env/cli runtime-env gen-ts --help

    Usage: runtime-env gen-ts [options]
    
    generate a TypeScript file that provides the corresponding type definitions for the JavaScript file generated by the gen-js command
    
    Options:
      --output-file-path <outputFilePath>  specify the output file to be written instead of being piped to stdout
      -h, --help                           display help for command
    

Syntax

  • For HTML, you can use environment variables by a template:

    <!-- Syntax: <%= <globalVariableName>.<environmentVariableName> %> -->
    <script
      async
      src="https://www.googletagmanager.com/gtag/js?id=<%= runtimeEnv.TAG_ID %>"
    ></script>
  • For JavaScript, you can read environment variables through the globalThis property:

    // Syntax: globalThis.<globalVariableName>.<environmentVariableName>
    initializeApp(globalThis.runtimeEnv.FIREBASE_CONFIG);