Skip to content
/ envar Public

πŸš€ Envar makes it easy to add configurability to your Deno applications. It supports loading configuration from environment variables as well asspecifying default values.

License

Notifications You must be signed in to change notification settings

wuespace/envar

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@wuespace/envar

JSR Scope JSR JSR Score Deno CI Publish Workflow

πŸš€ Envar makes it easy to add configurability to your Deno applications. It supports loading configuration from environment variables as well as specifying default values. It even supports configuration values from files specified by environment variables to provide first-class support for secrets and the like in your Docker Swarm or Kubernetes deployments.

πŸ“¦ Usage

You can use Envar in your Deno application by importing it from the jsr:@wuespace/envar module.

Note

When you want to use envar in a bigger application, you should consider installing it using deno add jsr:@wuespace/envar to make sure you have a fixed version.

// Import the initVariable function from the module
import { initVariable, EnvNotSetError } from "jsr:@wuespace/envar";
import { z } from "npm:zod";

// Initialize the application variables
await initVariable("PORT", z.string().match(/^[0-9]{1,5}$/), "8080");
await initVariable("SECRET", z.string().min(32));
// At this point, we can rest assured that we have valid values for
// PORT and SECRET. Otherwise, the promises would have been rejected.

// Access the variables like you normally would.
// Everything's synchronous now, so it's quite easy.
console.log(Deno.env.get("PORT"));
console.log(Deno.env.get("SECRET"));

// For type safety, you'll need to check if it's undefined:
const port = Deno.env.get("PORT");
if (port == undefined) {
    // Automatically generate a nice error message for the user
    throw new EnvNotSetError("PORT");
}

// Alternatively, you can also use process.env in Deno 2.0+
console.log(process.env.PORT);

Warning

Do not access Deno.env.get() or similar functions at the top level of another module. Due to the import order, the environment variables may not have been initialized at that point. However, accessing the environment variable on-demand inside functions is not a big issue, as it is a synchronous operation.

πŸ” Variable Sources

But why does initVariable return a Promise? Because Envar doesn't just look at environment variables. It also supports loading variables from files. This asynchronous behavior ensures that all potential sources are checked and validated before the variable is set.

Let's illustrate this with an example:

await initVariable("PORT", z.string().match(/^[0-9]{1,5}$/), "8080");

This call attempts to load the PORT variable from the following sources, in order:

🌐 Environment Variables

First, Envar checks if an environment variable named PORT exists. If it does, the value is validated and used.

πŸ“‚ Files

If the PORT environment variable is not found, Envar looks for a variable named PORT_FILE. If PORT_FILE exists, Envar reads the file specified by this variable and uses its contents as the value for PORT.

Tip

This follows the convention described in the Docker Secrets documentation.

πŸ› οΈ Defaults

If neither an environment variable nor a file is found, the default value specified in the initVariable call ("8080" in this case) is used.

🚨 Validation

Regardless of the source, the value is validated using the validator provided in the initVariable call, which is typically a zod schema. If the value is invalid, an error is thrown, and the application should handle it accordingly.

The validator can also specify whether the variable can be undefined (if no value is found and there is no default value):

// Throws an error if the variable isn't set
await initVariable("SECRET", z.string()); 

// Uses the default value if SECRET is not set
await initVariable("SECRET", z.string(), 'xxx'); 

// Allows SECRET to be undefined without throwing an error
await initVariable("SECRET", z.string().optional()); 

πŸ”§ Built-in Validators

Note

This feature is available starting from version v1.1.0.

Envar provides several built-in validators to simplify common validation tasks:

  • REQUIRED - Ensures the variable is set and not undefined
  • OPTIONAL - Allows the variable to be undefined
  • REQUIRED_NON_EMPTY - Ensures the variable is set and not an empty string
  • OPTIONAL_NON_EMPTY - Allows the variable to be undefined or a non-empty string

Here's how you can use these validators:

import {
  initVariable,
  REQUIRED,
  OPTIONAL,
  REQUIRED_NON_EMPTY,
  OPTIONAL_NON_EMPTY,
} from "jsr:@wuespace/envar";

await initVariable("REQUIRED", REQUIRED); // Must be set
await initVariable("OPTIONAL", OPTIONAL); // Can be undefined
await initVariable("REQUIRED_NON_EMPTY", REQUIRED_NON_EMPTY); // Must be set and not empty
await initVariable("OPTIONAL_NON_EMPTY", OPTIONAL_NON_EMPTY); // Can be undefined or non-empty

πŸš€ Deployment Options

Due to Envar's flexible source system, you have multiple options for deploying your application. We'll demonstrate these with a Docker Compose configuration, but the same principles apply to other deployment methods as well.

πŸ’» Application

await initVariable("PORT", z.string().match(/^[0-9]{1,5}$/), "8080");
await initVariable("OAUTH_TOKEN", z.string());
await initVariable("DB_URI", z.string().url());
await initVariable("SECRET", z.string());
await initVariable("CONFIG", z.string());
await initVariable("ANOTHER_SECRET", z.string());

const rawConfig = Deno.env.get("CONFIG");
if (rawConfig == undefined) {
    throw new EnvNotSetError("CONFIG");
}
const config = JSON.parse(rawConfig);
console.log(config); // { key: "value" }

🐳 Docker Compose

name: My Application

services:
  my-service:
    image: my-service
    environment:
      - PORT # Loaded from environment. Defaults to 8080 if not set
      - SECRET_FILE=/run/secrets/my-secret # Mounted as secret file
      - OAUTH_TOKEN_FILE=/run/secrets/another-secret # Mounted as secret file
      - CONFIG_FILE=/my-service-config # Mounted as config file
      - DB_URI=${DB_URI:-mongodb://mongo:27017/my-database} # Default value
      - ANOTHER_SECRET=${ANOTHER_SECRET:?ANOTHER_SECRET is required} # Required variable
    secrets:
      - my-secret
      - another-secret
    configs:
      - my-service-config
  mongo:
    image: mongo

secrets:
  my-secret:
    file: ./secret.txt # Loaded from a file
  another-secret:
    environment: "OAUTH_TOKEN" # Loaded from an environment variable

configs:
  my-service-config:
      content: |
        {
          "key": "value"
        }

This configuration ensures that your application variables are securely managed and validated, leveraging Docker's secrets and configs features.

πŸ‘₯ Authors

This package was created and is maintained by WΓΌSpace e. V.

Its primary author is Zuri Klaschka.

πŸ“„ License

This package is licensed under the MIT license. See the LICENSE file for more information.

About

πŸš€ Envar makes it easy to add configurability to your Deno applications. It supports loading configuration from environment variables as well asspecifying default values.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks