@marc-newton/solid-class-js
v1.0.0
Published
> A robust TypeScript/JavaScript library for building SOLID, strictly-typed data models. Safely ingest, sanitize, and cast raw API responses into structured class instances.
Maintainers
Readme
Solid Class JS
A robust TypeScript/JavaScript library for building SOLID, strictly-typed data models. Safely ingest, sanitize, and cast raw API responses into structured class instances.
Solid Class JS provides a foundational base class and a suite of TypeScript decorators to help you map messy, untrusted data to strict, predictable data models. By defining the expected attributes, data types, and nested relationships in your classes, Solid Class JS automatically handles type-casting, strips out unrecognized keys, and enriches your data payloads.
Features
- SOLID by Design: Extend the core base class to keep your data models modular, single-purpose, and strictly defined.
- Safe Data Assimilation: The
assignmethod takes raw JSON/objects, matches them against your defined properties, and ignores any unexpected keys to prevent prototype pollution or data leaks. - Automatic Type Casting: Ensures incoming data matches your expected types (e.g., parsing numeric strings into actual JavaScript numbers).
- Decorator-Driven: Clean, declarative syntax using TypeScript experimental decorators to define types, default values, and data enrichment rules.
- Recursive Structures: Easily handle complex API responses with nested objects and arrays of class instances.
Installation
Use your preferred package manager to install solid-class-js.
npm install solid-class-jspnpm add solid-class-jsyarn add solid-class-jsbun add solid-class-jsNote: Because
solid-class-jsrelies on TypeScript decorators, you must enableexperimentalDecoratorsandemitDecoratorMetadatain yourtsconfig.json.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Quick Start
1. Define Your Models
Extend the BaseModel to inherit the assign functionality. Use decorators to define your property types and nested structures.
import { BaseModel, Cast, CastArray, Enrich } from 'solid-class-js';
// A nested class model
class Address extends BaseModel {
@Cast('string')
street!: string;
@Cast('string')
city!: string;
}
// Your primary data model
class User extends BaseModel {
@Cast('number')
id!: number;
@Cast('string')
username!: string;
// Use decorators to handle arrays of other class instances
@CastArray(() => Address)
addresses!: Address[];
// Use custom enrichment logic
@Enrich((data) => data.firstName + ' ' + data.lastName)
fullName!: string;
}2. Ingest API Data
When you receive a payload from your API, simply instantiate your class and use the assign method. Unmapped keys will be safely ignored, and nested arrays will automatically instantiate their respective classes.
// Messy API response with extra, unneeded data
const apiResponse = {
id: "123", // Note: String type from API, but we want a Number
username: "johndoe",
firstName: "John",
lastName: "Doe",
secretToken: "xyz123", // We don't want this in our model!
addresses: [
{ street: "123 Main St", city: "Manchester", extraKey: "ignore me" }
]
};
// Assimilate the data
const user = new User();
user.assign(apiResponse);
console.log(user.id);
// Output: 123 (Successfully cast to a number)
console.log(user.secretToken);
// Output: undefined (Ignored because it wasn't defined in the User class)
console.log(user.fullName);
// Output: "John Doe" (Generated via the @Enrich decorator)
console.log(user.addresses[0] instanceof Address);
// Output: true (Recursively mapped to the Address class)API Reference
BaseModel
The core class your models must extend.
assign(data: Partial<this>): this- Parses the incoming object, applying validation, casting, and decorator logic based on the class definition. Returns the mutated class instance for chaining.commit(): void- Snapshots the current instance state implicitly natively creating a baseline to revert back to safely. Useful for capturing exactly what a fresh API GET resolves as or solidifying the state explicitly following a database POST.reset(): void- Drops all current patch modifications natively running through.assign()forcefully rolling the data state natively backward safely to the last explicitlycommit()ted snapshot!
Decorators
@Cast(type: 'string' | 'number' | 'boolean')- Forces the incoming value into the specified primitive type.@CastDate()- Instantiates a nativeDateobject from numeric timestamps or valid ISO 8601 strings.@CastObject(() => ClassName)- Maps an incoming object to a specificsolid-class-jsmodel instance.@CastArray(() => ClassName)- Maps an incoming array of objects to an array of specificsolid-class-jsmodel instances.@Enrich(callback)- Computes a property's value dynamically based on the raw incoming data.@MapFrom(alias: string)- Ingests a missing property securely from an alternative API layout sequence (e.g. grabbingfirst_nameto map ontofirstName).@Exclude(...contexts: string[])- Hides a specific parameter universally()or explicitly from a strictly contextual evaluation sequence('POST', 'PUT')during serialization hooks.
Validation Decorators
Use these to enforce strict value conditions on defined properties. If any conditions are not met during assign(), a structured ValidationErrorsList exception is thrown containing an .errors array with every failed ValidationError.
@Default(value: any)- Assigns a fallback value before validation executing if the JSON payload is missing the property.@IsRequired()- Throws if the property is missing from the API payload (even after Defaults evaluate).@MinLength(length: number)- Enforces an array or string payload contains at leastlengthitems/characters.@MaxLength(length: number)- Enforces an array or string payload contains a maximum oflengthitems/characters.@Min(value: number)- Enforces a primitive number is no less thanvalue.@Max(value: number)- Enforces a primitive number is no greater thanvalue.@Matches(pattern: RegExp)- Tests a parsed string against a regular expression.@IsEmail()- A strict check assuring the string satisfies a standard format for an Email Address.@IsUrl()- A strict check assuring the string resolves securely into a native URL instance.@CustomValidator((value, instance) => boolean | string)- Escapes structural checking completely, passing the propertyvalueand parent modelinstanceto evaluate explicitly scoped generic business rules.
.toJSON(context?: string)
By default, standard stringification securely calls .toJSON() across objects recursively. You may extract custom .toJSON('POST') payloads that securely iterate properties conditionally ignoring specific targets marked via explicitly identical context-mapped @Exclude() targets.
Testing
To run the test suite, ensure you have installed the dev dependencies and run:
npm run testLicense
MIT
Publish
npm publish --access public