evp-ts is a lightweight and easy-to-use library for parsing environment variables in TypeScript projects. It provides a simple way to collect and validate environment variables.
This package is inspired by zod and EVP, an environment variable parser library for Haskell.
- Features
- Installation
- Usage
- Supported Types
- Modifiers
- Error Handling
- Generating Help Text
- Discriminated Unions
- Customising the Logger
- Detecting Unused Variables
- License
- πΎ Low dependency footprint
- π§Ή Clutter-free code
- πͺΊ Supports nested structure
- π§© Well-typed interface
- βοΈ Derive types from the parser
- π Explicit logging of parsed environment variables
- π Hiding sensitive values (e.g. API keys) from logs
- π‘οΈ Handle errors comprehensively
- π Generate dotenv-style help text from the parser
- π Dynamically toggle between different sets of environment variables (discriminated unions)
- ποΈ Detect unused environment variables
# Using npm
npm install evp-ts
# Using yarn
yarn add evp-ts
# Using pnpm
pnpm add evp-ts
# Using bun
bun add evp-ts
Here's an example of how to use evp-ts in your TypeScript project:
import { EVP } from 'evp-ts';
const parser = EVP.object({
API_ENDPOINT: EVP.string(),
API_TOKEN: EVP.string().secret(),
HTTP_PORT: EVP.number(),
DEBUG_MODE: EVP.boolean().default(false),
});
type Config = EVP.infer<typeof parser>;
const result: Config = parser.parse();
console.log(result);
Example output:
[EVP] API_ENDPOINT=https://example.com
[EVP] API_TOKEN=<SHA256:fcf730b6>
[EVP] HTTP_PORT=8080
[EVP] DEBUG_MODE=false (default)
{
API_ENDPOINT: "https://example.com",
API_TOKEN: "secret123",
HTTP_PORT: 8080,
DEBUG_MODE: false
}
evp-ts supports the following types for parsing environment variables:
EVP.string()
: Get the value as a string.EVP.number()
: Parses the value as a number.EVP.boolean()
: Parses the value as a boolean (true
,yes
, and1
are parsed astrue
, whilefalse
,no
, and0
are parsed asfalse
).EVP.object()
: Defines a nested object structure for grouping related environment variables.EVP.enum()
: Validates that the value matches one of the specified options.
evp-ts provides additional options for configuring the behavior of environment variable parsing:
.default(value)
: Specifies a default value to use if the environment variable is not set..secret()
: Logs its SHA-256 hash instead of the actual value..optional()
: Marks the environment variable as optional, allowing it to be missing without causing an error..env(name)
: Specifies the name of the environment variable to use for parsing..description(text)
: Adds a description that appears in the help text..metavar(name)
: Customizes the placeholder shown in help text.
evp-ts provides error handling through the safeParse
method:
import { EVP } from 'evp-ts';
const parser = EVP.object({
PORT: EVP.number(),
API_KEY: EVP.string().secret(),
});
const result = parser.safeParse();
if (!result.success) {
console.error('Configuration error:', result.error.message);
process.exit(1);
}
// Use the validated config
const config = result.data;
Example error output:
[EVP] PORT=invalid_port ERROR: invalid number
[EVP] API_KEY=undefined ERROR: missing environment variable
Configuration error: Unable to fill the following fields: PORT, API_KEY
parser.describe()
generates a dotenv-style help text from the parser.
For this purpose, .description(text)
and .metavar(name)
methods are provided.
If .metavar(name)
is not specified, the default value or the type name is used as the metavariable name.
import { EVP } from 'evp-ts';
const parser = EVP.object({
API_ENDPOINT: EVP.string().description('The base URL of the API'),
API_TOKEN: EVP.string().secret().metavar('TOKEN'),
HTTP_PORT: EVP.number().description('The port number to listen on'),
DEBUG_MODE: EVP.boolean().default(false),
});
console.log(parser.describe());
Output:
# The base URL of the API
API_ENDPOINT=<string>
API_TOKEN=TOKEN
# The port number to listen on
HTTP_PORT=<number>
DEBUG_MODE=false
The EVP.union()
function allows you to switch between different sets of environment variables based on a discriminator value:
import { EVP } from 'evp-ts';
const parser = EVP.object({
DATABASE_BACKEND: EVP.union({
mysql: EVP.object({
host: EVP.string().env('MYSQL_HOST').default('localhost'),
port: EVP.number().env('MYSQL_PORT').default(3306),
}).description('MySQL database connection settings'),
sqlite: EVP.object({
path: EVP.string().env('SQLITE_PATH'),
}),
}).tag('backend')
});
MySQL configuration output:
[EVP] DATABASE_BACKEND=mysql
[EVP] MYSQL_HOST=localhost (default)
[EVP] MYSQL_PORT=3306 (default)
{
DATABASE_BACKEND: {
backend: "mysql",
host: "localhost",
port: 3306
}
}
SQLite configuration output:
[EVP] DATABASE_BACKEND=sqlite
[EVP] SQLITE_PATH=/path/to/db.sqlite
{
DATABASE_BACKEND: {
backend: "sqlite",
path: "/path/to/db.sqlite"
}
}
You can use either the default console logger or a custom logger like Winston:
import { EVP } from 'evp-ts';
import * as winston from 'winston';
const logger = winston.createLogger({
transports: [
new winston.transports.Console({
format: winston.format.simple()
})
]
});
const parser = EVP.object({
LOG_LEVEL: EVP.enum(['error', 'warn', 'info', 'debug']).default('info'),
}).logger(logger);
const result = parser.parse();
logger.level = result.LOG_LEVEL;
Winston logger output:
info: LOG_LEVEL=debug
Typos in environment variable names can lead to bugs that are difficult to detect, especially when the variable is optional.
To detect unused environment variables, use assumePrefix()
and rejectUnused()
:
import { EVP } from 'evp-ts';
const parser = EVP.object({
APP_FOO: EVP.string(),
})
.logger(logger)
.assumePrefix('APP_')
.rejectUnused();
const result = parser.parse({
APP_FOO: 'foo',
APP_BAR: 'bar', // This will be detected as unused
HOME: '/home/user', // This will be ignored (no prefix)
});
Output:
error: Unused variables: APP_BAR
## License
evp-ts is open-source software licensed under the [MIT License](https://opensource.org/licenses/MIT).