Metadata Storage
The MetadataStorage class is the central registry for all entity metadata in NestJS CRUD Automator. It stores property configurations defined with @ApiPropertyDescribe
and provides methods to retrieve this metadata during DTO generation and controller setup.
How It Works
When you decorate entity properties with @ApiPropertyDescribe
, the decorator stores metadata in the MetadataStorage singleton:
@Entity("users")
export class UserEntity {
@Column()
@ApiPropertyDescribe({
type: EApiPropertyDescribeType.STRING,
description: "Username",
format: EApiPropertyStringType.STRING,
minLength: 3,
maxLength: 20,
})
username: string;
}
Behind the scenes, @ApiPropertyDescribe
calls:
MetadataStorage.getInstance().setMetadata(
"UserEntity",
"username",
"api-property-describe",
{
type: EApiPropertyDescribeType.STRING,
description: "Username",
// ... other properties
}
);
Singleton Pattern
MetadataStorage uses the singleton pattern to ensure a single instance across the application:
const storage = MetadataStorage.getInstance();
This guarantees that metadata is stored and retrieved consistently regardless of where it is accessed.
Storage Structure
Metadata is organized hierarchically:
MetadataStorage
├── EntityName1
│ ├── propertyName1
│ │ └── metadata-key → metadata value
│ └── propertyName2
│ └── metadata-key → metadata value
└── EntityName2
└── propertyName1
└── metadata-key → metadata value
Metadata Retrieval
Get Property Metadata
Retrieve metadata for a specific property:
const metadata = MetadataStorage.getInstance().getMetadata(
"UserEntity",
"username",
"api-property-describe"
);
// Returns:
// {
// type: EApiPropertyDescribeType.STRING,
// description: "Username",
// format: EApiPropertyStringType.STRING,
// minLength: 3,
// maxLength: 20,
// }
Get All Entity Metadata
Retrieve metadata for all properties of an entity:
const allMetadata = MetadataStorage.getInstance().getAllMetadata(
"UserEntity"
);
// Returns object with all properties and their metadata
Check Metadata Existence
Verify if metadata exists for a property:
const exists = MetadataStorage.getInstance().hasMetadata(
"UserEntity",
"username",
"api-property-describe"
);
Usage in Library
The library uses MetadataStorage internally for:
DTO Generation
When generating DTOs, the library retrieves property metadata to determine:
- Which properties to include
- Validation rules to apply
- Swagger documentation to generate
- Default values and examples
// Simplified example of DTO generation
function generateDto(entityName: string) {
const metadata = MetadataStorage.getInstance().getAllMetadata(entityName);
for (const [propertyName, propertyMetadata] of Object.entries(metadata)) {
// Apply validation decorators based on metadata
// Generate Swagger documentation
// Set default values
}
}
Controller Factory
The ApiControllerFactory uses metadata to:
- Configure route handlers
- Generate request/response DTOs
- Set up validation pipelines
- Configure Swagger endpoints
Entity Analysis
The analyzeEntityMetadata
utility function uses MetadataStorage to:
- Identify primary keys
- Detect relations
- Validate property configurations
- Build entity information objects
Direct Usage
While typically you interact with MetadataStorage through decorators, you can access it directly:
import { MetadataStorage } from "@elsikora/nestjs-crud-automator";
const storage = MetadataStorage.getInstance();
// Store custom metadata
storage.setMetadata(
"CustomEntity",
"customProperty",
"custom-key",
{ customData: "value" }
);
// Retrieve custom metadata
const customMetadata = storage.getMetadata(
"CustomEntity",
"customProperty",
"custom-key"
);
Metadata Keys
The library uses predefined metadata keys:
api-property-describe
- Property configuration from @ApiPropertyDescribe- Additional keys for internal use by specific features
Thread Safety
MetadataStorage is designed to be thread-safe in Node.js single-threaded environment. The singleton instance is created once and reused throughout the application lifecycle.
Memory Considerations
Metadata is stored in memory for the lifetime of the application. This is typically not a concern as:
- Metadata is defined at compile time
- The amount of metadata is proportional to entity complexity
- No dynamic metadata accumulation occurs during runtime
Debugging Metadata
To inspect stored metadata during development:
import { MetadataStorage } from "@elsikora/nestjs-crud-automator";
// Get all metadata for an entity
const userMetadata = MetadataStorage.getInstance().getAllMetadata("UserEntity");
console.log("User Entity Metadata:", JSON.stringify(userMetadata, null, 2));
Metadata Lifecycle
- Application Startup: Entity classes are loaded, decorators execute
- Decorator Execution:
@ApiPropertyDescribe
stores metadata in MetadataStorage - Controller Initialization: ApiControllerFactory retrieves metadata for DTO generation
- Runtime: Metadata is read for validation, transformation, and Swagger documentation
- Application Shutdown: Metadata is garbage collected with the application
Best Practices
- Always use decorators: Prefer
@ApiPropertyDescribe
over direct MetadataStorage manipulation - Avoid runtime modifications: Metadata should be static and defined at design time
- Use consistent entity names: Ensure entity class names match the names used in metadata queries
- Leverage type safety: Use TypeScript types when working with metadata
Extending MetadataStorage
For advanced use cases, you can extend metadata storage functionality:
import { MetadataStorage } from "@elsikora/nestjs-crud-automator";
class CustomMetadataManager {
private storage = MetadataStorage.getInstance();
storeCustomMetadata(
entityName: string,
propertyName: string,
customData: any
): void {
this.storage.setMetadata(
entityName,
propertyName,
"custom-metadata-key",
customData
);
}
getCustomMetadata(entityName: string, propertyName: string): any {
return this.storage.getMetadata(
entityName,
propertyName,
"custom-metadata-key"
);
}
}
Next Steps
- Entities - Configure entity properties
- DTOs - Understand DTO generation
- API Reference - Classes - MetadataStorage API reference