Skip to content

ikelaiah/dotenv-fp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

17 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐ŸŒฟ 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. Perfect for managing configuration in your Pascal applications without hardcoding sensitive data.

License: MIT Free Pascal Lazarus Supports Windows Supports Linux Version No Dependencies Documentation Status

โœจ 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:

# 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

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

# ๐Ÿ’ฌ 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

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

// ๐Ÿ“ 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

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

// ๐Ÿ” 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+)

// ๐Ÿ’พ 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:

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:

# 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
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

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 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:

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 file for details.

๐Ÿ™ Acknowledgments

  • Inspired by python-dotenv
  • Built for the awesome Free Pascal community

Happy coding! ๐Ÿš€

About

A feature-rich dotenv library for Free Pascal: load environment variables from .env files with type-safe getters, validation, interpolation, and zero memory management.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages