|
1 | | -# Go env |
| 1 | +# GoEnv |
2 | 2 |
|
3 | | -A lightweight Go package that loads environment variables from an .env file, which can be located anywhere within the |
4 | | -project's structure. |
| 3 | +A lightweight, intelligent Go library for loading environment variables from `.env` files with advanced features and robust error handling. |
5 | 4 |
|
6 | | -This package has no external dependencies. |
| 5 | +[](https://golang.org/) |
| 6 | +[](LICENSE) |
| 7 | +[](https://goreportcard.com/report/github.com/raiesbo/goenv) |
7 | 8 |
|
8 | | -## Install |
| 9 | +## Features |
9 | 10 |
|
10 | | -```shell |
| 11 | +- **Smart Discovery**: Recursively searches directories for `.env` files |
| 12 | +- **Flexible Configuration**: Support for multiple file patterns and loading strategies |
| 13 | +- **Type Safety**: Built-in converters for strings, integers, floats, and booleans |
| 14 | +- **Quoted Values**: Handles single and double-quoted strings with spaces |
| 15 | +- **Cycle Detection**: Prevents infinite loops with symbolic links |
| 16 | +- **Depth Limiting**: Configurable search depth for performance |
| 17 | +- **Error Context**: Detailed error messages with file and line information |
| 18 | +- **Zero Dependencies**: Pure Go implementation with no external dependencies |
| 19 | + |
| 20 | +## Quick Start |
| 21 | + |
| 22 | +### Installation |
| 23 | + |
| 24 | +```bash |
11 | 25 | go get github.com/raiesbo/goenv |
12 | 26 | ``` |
13 | 27 |
|
14 | | -## Examples |
| 28 | +### Basic Usage |
| 29 | + |
| 30 | +Create a `.env` file in your project: |
| 31 | + |
| 32 | +```env |
| 33 | +# Database configuration |
| 34 | +DB_HOST=localhost |
| 35 | +DB_PORT=5432 |
| 36 | +DB_NAME=myapp |
| 37 | +DB_SSL=true |
| 38 | +
|
| 39 | +# API settings |
| 40 | +API_KEY="your-secret-api-key" |
| 41 | +API_TIMEOUT=30 |
| 42 | +DEBUG_MODE=false |
| 43 | +
|
| 44 | +# Complex values with quotes |
| 45 | +MESSAGE='Hello, World!' |
| 46 | +DESCRIPTION="This is a quoted string with spaces" |
| 47 | +``` |
15 | 48 |
|
16 | | -As easy as: |
| 49 | +Load and use environment variables in your Go application: |
17 | 50 |
|
18 | 51 | ```go |
19 | 52 | package main |
20 | 53 |
|
21 | 54 | import ( |
22 | | - "goenv" |
| 55 | + "fmt" |
| 56 | + "log" |
| 57 | + |
| 58 | + "github.com/raiesbo/goenv" |
23 | 59 | ) |
24 | 60 |
|
25 | 61 | func main() { |
| 62 | + // Load .env file automatically |
26 | 63 | if err := goenv.Load(); err != nil { |
27 | | - panic(err) |
| 64 | + log.Fatal("Error loading .env file:", err) |
| 65 | + } |
| 66 | + |
| 67 | + // Get values with type conversion and fallbacks |
| 68 | + dbHost := goenv.GetString("DB_HOST", "localhost") |
| 69 | + dbPort := goenv.GetInt("DB_PORT", 3306) |
| 70 | + sslEnabled := goenv.GetBool("DB_SSL", false) |
| 71 | + timeout := goenv.GetFloat("API_TIMEOUT", 10.0) |
| 72 | + |
| 73 | + fmt.Printf("Connecting to %s:%d (SSL: %v)\n", dbHost, dbPort, sslEnabled) |
| 74 | + fmt.Printf("API timeout: %.1fs\n", timeout) |
| 75 | + |
| 76 | + // Get required variables (panics if not found) |
| 77 | + apiKey := goenv.MustGetString("API_KEY") |
| 78 | + fmt.Printf("API Key loaded: %s...\n", apiKey[:8]) |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +## Advanced Configuration |
| 83 | + |
| 84 | +### Custom Configuration |
| 85 | + |
| 86 | +```go |
| 87 | +package main |
| 88 | + |
| 89 | +import ( |
| 90 | + "log" |
| 91 | + "github.com/raiesbo/goenv" |
| 92 | +) |
| 93 | + |
| 94 | +func main() { |
| 95 | + config := &goenv.Config{ |
| 96 | + EnvFiles: []string{".env", ".env.local", ".env.production"}, |
| 97 | + MaxDepth: 5, |
| 98 | + StopOnFirst: false, // Load all matching files |
| 99 | + } |
| 100 | + |
| 101 | + if err := goenv.LoadWithConfig(config); err != nil { |
| 102 | + log.Fatal("Failed to load environment:", err) |
28 | 103 | } |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +### Loading Specific Files |
| 108 | + |
| 109 | +```go |
| 110 | +// Load a specific .env file |
| 111 | +if err := goenv.LoadFile(".env.production"); err != nil { |
| 112 | + log.Fatal("Failed to load production config:", err) |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +### Environment-Specific Loading |
| 117 | + |
| 118 | +```go |
| 119 | +package main |
| 120 | + |
| 121 | +import ( |
| 122 | + "os" |
| 123 | + "github.com/raiesbo/goenv" |
| 124 | +) |
| 125 | + |
| 126 | +func main() { |
| 127 | + env := os.Getenv("GO_ENV") |
| 128 | + if env == "" { |
| 129 | + env = "development" |
| 130 | + } |
| 131 | + |
| 132 | + config := &goenv.Config{ |
| 133 | + EnvFiles: []string{".env", ".env." + env}, |
| 134 | + StopOnFirst: false, // Load both base and environment-specific |
| 135 | + } |
| 136 | + |
| 137 | + goenv.LoadWithConfig(config) |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +## Supported File Formats |
| 142 | + |
| 143 | +GoEnv supports various `.env` file formats: |
| 144 | + |
| 145 | +```env |
| 146 | +# Comments are supported |
| 147 | +# Empty lines are ignored |
| 148 | +
|
| 149 | +# Basic key-value pairs |
| 150 | +DATABASE_URL=postgres://localhost/mydb |
| 151 | +PORT=8080 |
| 152 | +
|
| 153 | +# Quoted strings (single or double quotes) |
| 154 | +SECRET_KEY="my-secret-key-with-spaces" |
| 155 | +MESSAGE='Hello, "World"!' |
| 156 | +
|
| 157 | +# Boolean values |
| 158 | +DEBUG=true |
| 159 | +PRODUCTION=false |
| 160 | +
|
| 161 | +# Numeric values |
| 162 | +MAX_CONNECTIONS=100 |
| 163 | +TIMEOUT=30.5 |
| 164 | +
|
| 165 | +# Empty values |
| 166 | +OPTIONAL_CONFIG= |
| 167 | +``` |
| 168 | + |
| 169 | +## API Reference |
| 170 | + |
| 171 | +### Loading Functions |
| 172 | + |
| 173 | +| Function | Description | |
| 174 | +|----------|-------------| |
| 175 | +| `Load()` | Load `.env` file using default configuration | |
| 176 | +| `LoadWithConfig(config *Config)` | Load with custom configuration | |
| 177 | +| `LoadFile(path string)` | Load a specific file | |
| 178 | + |
| 179 | +### Type Conversion Functions |
| 180 | + |
| 181 | +| Function | Description | Example | |
| 182 | +|----------|-------------|---------| |
| 183 | +| `GetString(key, fallback string)` | Get string value | `GetString("API_URL", "http://localhost")` | |
| 184 | +| `GetInt(key string, fallback int)` | Get integer value | `GetInt("PORT", 8080)` | |
| 185 | +| `GetBool(key string, fallback bool)` | Get boolean value | `GetBool("DEBUG", false)` | |
| 186 | +| `GetFloat(key string, fallback float64)` | Get float value | `GetFloat("RATE", 1.5)` | |
| 187 | +| `MustGetString(key string)` | Get required string (panics if missing) | `MustGetString("DATABASE_URL")` | |
| 188 | + |
| 189 | +### Configuration Options |
| 190 | + |
| 191 | +```go |
| 192 | +type Config struct { |
| 193 | + EnvFiles []string // File patterns to search for |
| 194 | + MaxDepth int // Maximum directory depth to search |
| 195 | + StopOnFirst bool // Stop after finding first file |
| 196 | +} |
| 197 | +``` |
| 198 | + |
| 199 | +## Use Cases |
| 200 | + |
| 201 | +### Web Applications |
| 202 | + |
| 203 | +```go |
| 204 | +package main |
| 205 | + |
| 206 | +import ( |
| 207 | + "fmt" |
| 208 | + "net/http" |
| 209 | + "github.com/raiesbo/goenv" |
| 210 | +) |
| 211 | + |
| 212 | +func main() { |
| 213 | + goenv.Load() |
| 214 | + |
| 215 | + port := goenv.GetString("PORT", "8080") |
| 216 | + dbURL := goenv.MustGetString("DATABASE_URL") |
29 | 217 |
|
30 | | - // After loading, the environment variables are ready to be used. |
31 | | - // Example: addr := os.Getenv("ADDR") |
| 218 | + fmt.Printf("Starting server on port %s\n", port) |
| 219 | + fmt.Printf("Database: %s\n", dbURL) |
| 220 | + |
| 221 | + http.ListenAndServe(":" + port, nil) |
| 222 | +} |
| 223 | +``` |
| 224 | + |
| 225 | +### Microservices Configuration |
| 226 | + |
| 227 | +```go |
| 228 | +type Config struct { |
| 229 | + ServiceName string |
| 230 | + Port int |
| 231 | + DatabaseURL string |
| 232 | + RedisURL string |
| 233 | + LogLevel string |
| 234 | + Debug bool |
| 235 | +} |
| 236 | + |
| 237 | +func LoadConfig() *Config { |
| 238 | + goenv.Load() |
| 239 | + |
| 240 | + return &Config{ |
| 241 | + ServiceName: goenv.GetString("SERVICE_NAME", "unknown"), |
| 242 | + Port: goenv.GetInt("PORT", 8080), |
| 243 | + DatabaseURL: goenv.MustGetString("DATABASE_URL"), |
| 244 | + RedisURL: goenv.GetString("REDIS_URL", "redis://localhost:6379"), |
| 245 | + LogLevel: goenv.GetString("LOG_LEVEL", "info"), |
| 246 | + Debug: goenv.GetBool("DEBUG", false), |
| 247 | + } |
32 | 248 | } |
33 | 249 | ``` |
| 250 | + |
| 251 | +### Docker Integration |
| 252 | + |
| 253 | +```dockerfile |
| 254 | +# Dockerfile |
| 255 | +FROM golang:1.21-alpine |
| 256 | +WORKDIR /app |
| 257 | +COPY . . |
| 258 | +RUN go build -o myapp |
| 259 | + |
| 260 | +# Use .env file in development |
| 261 | +CMD ["./myapp"] |
| 262 | +``` |
| 263 | + |
| 264 | +```yaml |
| 265 | +# docker-compose.yml |
| 266 | +version: '3.8' |
| 267 | +services: |
| 268 | + app: |
| 269 | + build: . |
| 270 | + env_file: |
| 271 | + - .env |
| 272 | + - .env.local |
| 273 | + ports: |
| 274 | + - "${PORT:-8080}:8080" |
| 275 | +``` |
| 276 | +
|
| 277 | +## Testing |
| 278 | +
|
| 279 | +GoEnv includes comprehensive tests covering all functionality: |
| 280 | +
|
| 281 | +```bash |
| 282 | +# Run tests |
| 283 | +go test -v |
| 284 | + |
| 285 | +# Run tests with coverage |
| 286 | +go test -cover |
| 287 | + |
| 288 | +# Run benchmarks |
| 289 | +go test -bench=. |
| 290 | +``` |
| 291 | + |
| 292 | +## Error Handling |
| 293 | + |
| 294 | +GoEnv provides detailed error messages for debugging: |
| 295 | + |
| 296 | +```go |
| 297 | +if err := goenv.Load(); err != nil { |
| 298 | + // Errors include file path and line numbers |
| 299 | + fmt.Printf("Failed to load .env: %v\n", err) |
| 300 | + // Example: "invalid format in /path/.env at line 5: INVALID_LINE" |
| 301 | +} |
| 302 | +``` |
| 303 | + |
| 304 | +## Comparison with Other Libraries |
| 305 | + |
| 306 | +| Feature | GoEnv | godotenv | viper | |
| 307 | +|---------|-------|----------|-------| |
| 308 | +| File Discovery | ✅ Recursive | ❌ Manual path | ✅ Multiple sources | |
| 309 | +| Type Conversion | ✅ Built-in | ❌ Manual | ✅ Comprehensive | |
| 310 | +| Quoted Strings | ✅ Yes | ✅ Yes | ✅ Yes | |
| 311 | +| Error Context | ✅ Detailed | ❌ Basic | ✅ Good | |
| 312 | +| Zero Dependencies | ✅ Yes | ✅ Yes | ❌ Many deps | |
| 313 | +| Configuration | ✅ Flexible | ❌ Limited | ✅ Extensive | |
| 314 | +| Performance | ✅ Fast | ✅ Fast | ⚠️ Moderate | |
| 315 | + |
| 316 | +## Best Practices |
| 317 | + |
| 318 | +1. **Environment-Specific Files**: Use `.env.development`, `.env.production`, etc. |
| 319 | +2. **Never Commit Secrets**: Add `.env*` to your `.gitignore` |
| 320 | +3. **Provide Fallbacks**: Always use fallback values for non-critical settings |
| 321 | +4. **Validate Required Variables**: Use `MustGetString()` for essential configuration |
| 322 | +5. **Document Variables**: Create a `.env.example` file with all required variables |
| 323 | + |
| 324 | +### Example `.env.example` |
| 325 | + |
| 326 | +```env |
| 327 | +# Database Configuration |
| 328 | +DATABASE_URL=postgres://username:password@localhost:5432/dbname |
| 329 | +
|
| 330 | +# API Keys |
| 331 | +API_KEY=your-api-key-here |
| 332 | +SECRET_KEY=your-secret-key-here |
| 333 | +
|
| 334 | +# Application Settings |
| 335 | +PORT=8080 |
| 336 | +DEBUG=false |
| 337 | +LOG_LEVEL=info |
| 338 | +
|
| 339 | +# External Services |
| 340 | +REDIS_URL=redis://localhost:6379 |
| 341 | +SMTP_HOST=smtp.example.com |
| 342 | +SMTP_PORT=587 |
| 343 | +``` |
| 344 | + |
| 345 | +## 🤝 Contributing |
| 346 | + |
| 347 | +Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. |
| 348 | + |
| 349 | +1. Fork the repository |
| 350 | +2. Create your feature branch (`git checkout -b feature/amazing-feature`) |
| 351 | +3. Commit your changes (`git commit -m 'Add some amazing feature'`) |
| 352 | +4. Push to the branch (`git push origin feature/amazing-feature`) |
| 353 | +5. Open a Pull Request |
| 354 | + |
| 355 | +## 📄 License |
| 356 | + |
| 357 | +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. |
| 358 | + |
| 359 | +## 🙏 Acknowledgments |
| 360 | + |
| 361 | +- Inspired by the Node.js [dotenv](https://github.com/motdotla/dotenv) library |
| 362 | +- Thanks to the Go community for feedback and contributions |
| 363 | + |
| 364 | +--- |
| 365 | + |
| 366 | +**GoEnv** - Making environment configuration simple and reliable for Go applications. |
0 commit comments