Skip to content

Commit 49a81b1

Browse files
committed
docs: add best practice for environment variable key and value transforms with keyTransformer and @type usage
1 parent 1b3b34f commit 49a81b1

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,74 @@ TypedConfigModule.forRoot({
488488
});
489489
```
490490

491+
## Handling Environment Variable Key and Value Transforms (Best Practice)
492+
493+
When using multiple loaders (such as local YAML files for development and environment variables for production), it is common to need to map environment variables like `DATABASE__USER_NAME` to camelCase config properties like `userName`. Here is the recommended approach:
494+
495+
1. **Use `keyTransformer` to unify key style**
496+
497+
The `keyTransformer` option of `dotenvLoader` allows you to automatically convert environment variable names to camelCase. For example:
498+
499+
```ts
500+
TypedConfigModule.forRoot({
501+
schema: RootConfig,
502+
load: [
503+
fileLoader(),
504+
dotenvLoader({
505+
separator: '__',
506+
keyTransformer: (key) =>
507+
key
508+
.toLowerCase()
509+
.replace(/(?<!_)_([a-z])/g, (_, p1) => p1.toUpperCase()),
510+
}),
511+
],
512+
});
513+
```
514+
515+
With this, `DATABASE__USER_NAME=testuser` will be correctly mapped to the `userName` property.
516+
517+
2. **Use `@Type` decorator to ensure value type conversion**
518+
519+
Since environment variables are always loaded as strings, it is recommended to use the `@Type` decorator on config class properties to ensure correct type conversion:
520+
521+
```ts
522+
import { IsNumber, IsString } from 'class-validator';
523+
import { Type } from 'class-transformer';
524+
525+
export class DatabaseConfig {
526+
@IsNumber()
527+
@Type(() => Number)
528+
public readonly port!: number;
529+
530+
@IsString()
531+
public readonly userName!: string;
532+
}
533+
```
534+
535+
3. **Full example with multiple loaders**
536+
537+
```ts
538+
TypedConfigModule.forRoot({
539+
schema: RootConfig,
540+
load: [
541+
fileLoader(), // Local config for development
542+
dotenvLoader({
543+
separator: '__',
544+
keyTransformer: (key) =>
545+
key
546+
.toLowerCase()
547+
.replace(/(?<!_)_([a-z])/g, (_, p1) => p1.toUpperCase()),
548+
}), // Environment variables for production
549+
],
550+
});
551+
```
552+
553+
> **Tip:**
554+
> - Adjust the `keyTransformer` function to match your naming conventions as needed.
555+
> - It is recommended to document this usage in your codebase for team clarity and maintainability.
556+
557+
---
558+
491559
### Using custom loader
492560

493561
If native loaders provided by `nest-typed-config` can't meet your needs, you can implement a custom loader. This can be achieved by providing a function which returns the configuration object synchronously or asynchronously through the `load` option. For example:

0 commit comments

Comments
 (0)