https://github.com/ikelaiah/dotenv-fp
A feature-rich dotenv library for Free Pascal: load environment variables from .env files with type-safe getters, validation, interpolation, and zero memory management.
https://github.com/ikelaiah/dotenv-fp
config configuration cross-platform dotenv env environment-variables fpc freepascal lazarus library object-pascal pascal
Last synced: 28 days ago
JSON representation
A feature-rich dotenv library for Free Pascal: load environment variables from .env files with type-safe getters, validation, interpolation, and zero memory management.
- Host: GitHub
- URL: https://github.com/ikelaiah/dotenv-fp
- Owner: ikelaiah
- License: mit
- Created: 2025-12-10T19:00:19.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2025-12-15T20:03:56.000Z (about 2 months ago)
- Last Synced: 2025-12-19T03:50:42.909Z (about 2 months ago)
- Topics: config, configuration, cross-platform, dotenv, env, environment-variables, fpc, freepascal, lazarus, library, object-pascal, pascal
- Language: Pascal
- Homepage:
- Size: 573 KB
- Stars: 12
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# ๐ฟ dotenv-fp
> **Load environment variables from `.env` files in Free Pascal โ the easy way!**
A feature-rich dotenv library for **Free Pascal 3.2.2+** inspired by [python-dotenv](https://github.com/theskumar/python-dotenv). Perfect for managing configuration in your Pascal applications without hardcoding sensitive data.
[](https://opensource.org/licenses/MIT)
[](https://www.freepascal.org/)
[](https://www.lazarus-ide.org/)


[](CHANGELOG.md)

[](docs/)
[]()
## โจ Features
| Feature | Description |
|---------|-------------|
| ๐ **Load `.env` files** | Read configuration from `.env` files |
| ๐ **Variable interpolation** | Use `${VAR}` or `$VAR` syntax to reference other variables |
| ๐ **Multi-line values** | Support for values spanning multiple lines |
| ๐ฏ **Quoted values** | Single quotes, double quotes, or unquoted |
| ๐ฌ **Comments** | Use `#` for comments (line or inline) |
| ๐ **Shell compatible** | Supports `export` prefix for shell compatibility |
| ๐ **Type-safe getters** | `GetInt()`, `GetBool()`, `GetFloat()`, `GetArray()` |
| โก **Default values** | Fallback values when keys are missing |
| โ
**Validation** | Check for required variables before running |
| ๐ **Multiple files** | Load `.env`, `.env.local`, `.env.production`, etc. |
| ๐ **Environment-aware** | Auto-load `.env.{environment}` files (v1.1.0+) |
| ๐พ **Save to file** | Generate `.env` files programmatically (v1.1.0+) |
| ๐ **Generate examples** | Create `.env.example` for version control (v1.1.0+) |
| ๐ฌ **Interactive prompts** | `GetOrPrompt()` for first-run setup (v1.1.0+) |
| ๐ท๏ธ **Key prefixing** | Add prefixes like `APP_` to all loaded keys |
| ๐งน **Zero memory leaks** | Uses advanced records โ no manual `Free` calls! |
| ๐ฆ **Zero dependencies** | Only standard FPC units |
## ๐ Quick Start
### Installation
1. Copy `src/DotEnv.pas` to your project
*โ or add the `src` folder to your unit search path*
2. Add `DotEnv` to your `uses` clause
That's it! No package manager needed. ๐
### Your First `.env` File
Create a `.env` file in your project root:
```bash
# Database settings
DATABASE_URL=postgresql://localhost/mydb
DB_POOL_SIZE=10
# Server configuration
PORT=3000
DEBUG=true
# Secrets (never commit these!)
SECRET_KEY="super-secret-key-here"
```
### Load It in Pascal
```pascal
program MyApp;
{$mode objfpc}{$H+}{$J-}
uses
DotEnv;
var
Env: TDotEnv;
begin
// Create and load .env file
Env := TDotEnv.Create;
Env.Load; // Loads .env from current directory
// Read values with type safety
WriteLn('Database: ', Env.Get('DATABASE_URL'));
WriteLn('Port: ', Env.GetInt('PORT', 3000));
WriteLn('Debug mode: ', Env.GetBool('DEBUG', False));
WriteLn('Pool size: ', Env.GetInt('DB_POOL_SIZE', 5));
// No need to free - advanced records clean up automatically!
end.
```
## ๐ `.env` File Format
```bash
# ๐ฌ Comments start with #
# Empty lines are ignored
# Simple key-value pairs
DATABASE_URL=postgresql://localhost/mydb
PORT=3000
DEBUG=true
# ๐ฏ Quoted values (preserves spaces and special chars)
SECRET_KEY="my-secret-key"
MESSAGE='Hello, World!'
GREETING="Welcome to the app!"
# ๐ Variable interpolation
BASE_URL=https://api.example.com
API_ENDPOINT=${BASE_URL}/v1/users
FULL_URL=$BASE_URL/health
# ๐ Multi-line values (use quotes)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
...more lines...
-----END RSA PRIVATE KEY-----"
# ๐ Shell-compatible export syntax
export SHELL_VAR=works_in_bash_too
# ๐ Arrays (comma-separated, parsed with GetArray)
ALLOWED_HOSTS=localhost,127.0.0.1,example.com
FEATURES=auth,logging,cache
```
## ๐ API Reference
### Creating and Loading
```pascal
var
Env: TDotEnv;
Options: TDotEnvOptions;
begin
// ๐ข Simple usage
Env := TDotEnv.Create;
Env.Load; // Load .env
Env.Load('.env.local'); // Load specific file
// ๐ก With options
Options := TDotEnvOptions.Default;
Options.Override := True; // Override existing env vars
Options.Verbose := True; // Print debug info
Options.Prefix := 'APP_'; // Prefix all keys with APP_
Env := TDotEnv.CreateWithOptions(Options);
Env.Load;
// ๐ต Load multiple files (later files override earlier ones)
Env.LoadMultiple(['.env', '.env.local', '.env.development']);
// ๐ฃ Load from string (great for testing!)
Env.LoadFromString('KEY=value' + LineEnding + 'OTHER=test');
// ๐ NEW in v1.1.0: Environment-aware loading
Env.LoadForEnvironment('production'); // Loads .env + .env.production
Env.LoadForEnvironment(); // Auto-detects from APP_ENV or NODE_ENV
end;
```
### Getting Values
```pascal
// ๐ Strings
Env.Get('KEY'); // Returns '' if missing
Env.Get('KEY', 'default'); // Returns 'default' if missing
Env.GetRequired('KEY'); // Raises exception if missing
// ๐ข Integers
Env.GetInt('PORT'); // Returns 0 if missing/invalid
Env.GetInt('PORT', 3000); // Returns 3000 if missing/invalid
Env.GetIntRequired('PORT'); // Raises exception if missing/invalid
// โ
Booleans (recognizes: true/false, yes/no, 1/0, on/off)
Env.GetBool('DEBUG'); // Returns False if missing
Env.GetBool('DEBUG', True); // Returns True if missing
Env.GetBoolRequired('DEBUG'); // Raises exception if missing
// ๐ฌ Floats
Env.GetFloat('RATE'); // Returns 0.0 if missing/invalid
Env.GetFloat('RATE', 0.5); // Returns 0.5 if missing/invalid
Env.GetFloatRequired('RATE'); // Raises exception if missing/invalid
// ๐ Arrays (splits comma-separated values)
Hosts := Env.GetArray('ALLOWED_HOSTS'); // Split by comma
Tags := Env.GetArray('TAGS', ';'); // Split by semicolon
// ๐ฌ NEW in v1.1.0: Interactive prompts (for setup scripts)
DbUrl := Env.GetOrPrompt('DATABASE_URL',
'Enter database URL',
'postgres://localhost/mydb');
// Prompts user if DATABASE_URL is missing, uses existing value if present
```
### Validation
```pascal
var
Missing: TStringDynArray;
I: Integer;
begin
// โ
Check if all required keys exist
if Env.Validate(['DATABASE_URL', 'SECRET_KEY', 'PORT']) then
WriteLn('All required configuration present!')
else
begin
// ๐ Get list of missing keys
Missing := Env.GetMissing(['DATABASE_URL', 'SECRET_KEY', 'PORT']);
WriteLn('Missing configuration:');
for I := 0 to High(Missing) do
WriteLn(' - ', Missing[I]);
Halt(1);
end;
end;
```
### Utilities
```pascal
// ๐ Check if key exists
if Env.Has('OPTIONAL_FEATURE') then
EnableFeature;
// ๐ Get all keys and values
Keys := Env.Keys; // TStringDynArray of all key names
Values := Env.Values; // TStringDynArray of all values
Pairs := Env.AsArray; // TDotEnvPairArray with Key/Value records
// ๐ข Count loaded variables
WriteLn('Loaded ', Env.Count, ' environment variables');
// ๐ Debug output (shows all loaded key=value pairs)
WriteLn(Env.ToString);
// ๐ See which files were loaded
for I := 0 to High(Env.LoadedFiles) do
WriteLn('Loaded: ', Env.LoadedFiles[I]);
```
### File Operations (v1.1.0+)
```pascal
// ๐พ Save environment variables to a file
Env := TDotEnv.Create;
Env.SetToEnv('DATABASE_URL', 'postgres://localhost/mydb');
Env.SetToEnv('PORT', '3000');
Env.SetToEnv('DEBUG', 'true');
Env.Save('.env'); // Writes to .env file
// ๐ Generate .env.example for version control
Env.Load('.env');
Env.GenerateExample('.env', '.env.example');
// Creates .env.example with keys but empty values
// ๐ Environment-aware loading pattern
Env := TDotEnv.Create;
Env.LoadForEnvironment('development'); // Loads .env + .env.development
Env.LoadForEnvironment('production'); // Loads .env + .env.production
Env.LoadForEnvironment(); // Auto-detects from APP_ENV/NODE_ENV
// ๐ฌ Interactive setup script example
Env := TDotEnv.Create;
Env.Load('.env'); // Try to load existing config
DbUrl := Env.GetOrPrompt('DATABASE_URL',
'Enter database URL',
'postgres://localhost/mydb');
Port := Env.GetOrPrompt('PORT', 'Enter port', '3000');
Env.Save('.env'); // Save the configuration
WriteLn('Configuration saved!');
```
### Global Helpers (Simple API)
For quick scripts or simple applications:
```pascal
uses DotEnv;
begin
DotEnvLoad; // Load .env
DotEnvLoad('.env.local'); // Load specific file
WriteLn(DotEnvGet('DATABASE_URL')); // Get value
WriteLn(DotEnvGet('PORT', '3000')); // Get with default
DotEnvSet('RUNTIME_VAR', 'value'); // Set at runtime
end.
```
## ๐ Variable Interpolation
Reference other variables using `${VAR}` or `$VAR` syntax:
```bash
# Define base values
APP_NAME=MyAwesomeApp
APP_VERSION=1.0.0
BASE_URL=https://api.example.com
# Reference them in other values
APP_TITLE=${APP_NAME} v${APP_VERSION}
API_USERS=${BASE_URL}/users
API_HEALTH=$BASE_URL/health
# Also works with system environment variables!
HOME_CONFIG=${HOME}/.myapp/config
```
**Resolution order:**
1. Variables defined earlier in the same `.env` file
2. System environment variables
## โ๏ธ Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `Override` | `Boolean` | `False` | Override existing system environment variables |
| `Interpolate` | `Boolean` | `True` | Enable `${VAR}` variable interpolation |
| `Verbose` | `Boolean` | `False` | Print debug info while loading |
| `Prefix` | `String` | `''` | Add prefix to all loaded key names |
```pascal
var
Options: TDotEnvOptions;
begin
Options := TDotEnvOptions.Default; // Start with defaults
Options.Override := True;
Options.Prefix := 'MYAPP_';
Env := TDotEnv.CreateWithOptions(Options);
Env.Load;
// KEY=value in .env becomes accessible as MYAPP_KEY
WriteLn(Env.Get('MYAPP_KEY'));
end;
```
## โ ๏ธ Error Handling
```pascal
uses DotEnv;
var
Env: TDotEnv;
begin
Env := TDotEnv.Create;
Env.Load;
// ๐ด Handle missing required keys
try
Env.GetRequired('MISSING_KEY');
except
on E: EDotEnvMissingKey do
WriteLn('Configuration error: ', E.Message);
end;
// ๐ด Handle invalid type conversions
try
Env.GetIntRequired('NOT_A_NUMBER');
except
on E: EDotEnvParseError do
WriteLn('Parse error: ', E.Message);
end;
end.
```
## ๐ Comparison with Python dotenv
dotenv-fp is inspired by [python-dotenv](https://github.com/theskumar/python-dotenv) and provides equivalent core functionality, plus additional features suited for statically-typed Pascal development:
| Feature | python-dotenv | dotenv-fp |
|---------|:-------------:|:---------:|
| Load .env file | โ
| โ
|
| Variable interpolation | โ
| โ
|
| Multi-line values | โ
| โ
|
| Quoted values | โ
| โ
|
| Export prefix | โ
| โ
|
| Comments | โ
| โ
|
| Override mode | โ
| โ
|
| **Type-safe getters** | โ | โ
|
| **Built-in validation** | โ | โ
|
| **Array parsing** | โ | โ
|
| **Key prefixing** | โ | โ
|
| **Automatic memory management** | N/A | โ
|
## ๐งช Running Tests
The library includes a comprehensive test suite using FPCUnit:
```bash
cd tests
fpc TestRunner.pas
./TestRunner -a --format=plain
```
Expected output:
```
Time:00.075 N:96 E:0 F:0 I:0
TTestDotEnv Time:00.075 N:96 E:0 F:0 I:0
00.000 Test01_BasicParsing_SimpleKeyValue
00.000 Test02_BasicParsing_TrimmedValue
...
Number of run tests: 96
Number of errors: 0
Number of failures: 0
```
## ๐ Project Structure
```
dotenv-fp/
โโโ src/
โ โโโ DotEnv.pas # Main library unit
โโโ tests/
โ โโโ TestRunner.pas # FPCUnit test runner
โ โโโ DotEnv.Test.pas # Test cases
โโโ examples/
โ โโโ basic/ # Basic usage example
โ โโโ advanced/ # Advanced features example
โโโ docs/ # Documentation
โโโ .env.example # Example .env file
โโโ CHANGELOG.md
โโโ README.md
```
## ๐ก Tips for New Free Pascal Developers
1. **Advanced Records**: This library uses advanced records (`{$modeswitch advancedrecords}`), which means you don't need to call `.Free` โ memory is managed automatically!
2. **Mode ObjFPC**: Make sure your program uses `{$mode objfpc}{$H+}{$J-}` for compatibility.
3. **String Handling**: The `{$H+}` switch enables long strings (AnsiString) by default, which this library requires.
4. **File Paths**: Use forward slashes `/` or the `PathDelim` constant for cross-platform compatibility.
## ๐ค Contributing
Contributions are welcome! Feel free to:
- ๐ Report bugs
- ๐ก Suggest features
- ๐ง Submit pull requests
## ๐ License
MIT License โ See [LICENSE](LICENSE) file for details.
## ๐ Acknowledgments
- Inspired by [python-dotenv](https://github.com/theskumar/python-dotenv)
- Built for the awesome Free Pascal community
---
**Happy coding! ๐**