@gnoesiboe/entity-to-object-transformer
TypeScript icon, indicating that this package has built-in type declarations

2.3.0 • Public • Published

Entity to Object transformer

Transforms entities to native objects and back using a mapping. Enables you to use domain entities in your application, but store them as plain objects, and re-hydrate them when they come back from the database.

Features

  • Support for custom transformers to transform and reverse transform complex property values like value objects
  • Transforms nested entity relationship including child collections
  • Typescript types included
  • Well tested
  • By default, it throws when you forget or wrongly map a property, to prevent you from making mistakes

Example usage:

import EntityToObjectTransformer, { ObjectMapping } from '@gnoesiboe/entity-to-object-transformer'

type AuthorAsObjectType = {
  _id: string;
  name: string;
  createdAt: string;
};

type BlogItemAsObject = {
  _id: string;
  title: string;
  description: string;
  createdAt: string;
  author: AuthorAsObjectType;
}

const author = new Author(new Uuid(), 'Peter Pan')
const blogItem = new BlogItem(new Uuid(), 'Some title', 'Some description', author);

const mapping: ObjectMapping = {
    type: 'object',
    constructor: BlogItem,
    properties: {
        uuid: {
            type: 'property',
            as: '_id',
            transformer: new UuidToStringTransformer(),
        },
        _title: {
            type: 'property',
            as: 'title',
        },
        _description: {
            type: 'property',
            as: 'description',
        },
        createdAt: {
            type: 'property',
            transformer: new DateToStringTransformer(),
        },
        author: {
            type: 'object',
            constructor: Author,
            properties: {
                uuid: {
                    type: 'property',
                    as: '_id',
                    transformer: new UuidToStringTransformer(),
                },
                _name: {
                    as: 'name',
                    type: 'property',
                },
                createdAt: {
                    type: 'property',
                    transformer: new DateToStringTransformer(),
                },
            },
        },
    }
}

const transformer = new EntityToObjectTransformer<BlogItem, BlogItemAsObject>(mapping);

const blogItemAsObject = transformer.transform(blogItem);

/*
OUTPUT:

{
    _id: 'ae1a0d1a-2f6b-40e5-90b6-5b3d2f00e83a',
    title: 'Some title',
    description: 'Some description',
    createdAt: '2021-10-14T06:28:37.021Z',
    author: {
        _id: '352cc994-12c0-4e07-b837-0b4e6c31711b',
        name: 'Peter Pan',
        createdAt: '2021-10-14T06:29:00.841Z',
    },
}

 */

const blogItemAsModelAgain = transformer.reverseTransform(blogItemAsObject);

// OUTPUT: Your entity, re-hydrated again

Documentation

The EntityToObjectTransformer exposes two methods:

  1. transform → transforms an entity into an object by plucking and transforming the instances properties according to the supplied mapping
  2. reverseTransform → transforms an object into an entity by constructing it, and setting the properties on it, according to the supplied mapping

The mapping implements two types:

export type PropertyMapping = {
    type: 'property';
    as?: string;
    transformer?: PropValueTransformer;
};

export type ObjectMapping = {
    type: 'object';
    constructor: ClassConstructor;
    as?: string;
    properties: {
        [key: string]: PropertyMapping | ObjectMapping;
    };
    ignoredProperties?: string[];
};

Starting with an ObjectType, you can nest them together to form a mapping tree:

Example:

const mapping: ObjectMapping = {
    type: 'object',
    constructor: BlogItem,
    properties: {
        uuid: {
            type: 'property',
            as: '_id',
            transformer: new UuidToStringTransformer(),
        },
        _title: {
            type: 'property',
            as: 'title',
        },
        _description: {
            type: 'property',
            as: 'description',
        },
        createdAt: {
            type: 'property',
            transformer: new DateToStringTransformer(),
        },
        author: {
            type: 'object',
            constructor: Author,
            properties: {
                uuid: {
                    type: 'property',
                    as: '_id',
                    transformer: new UuidToStringTransformer(),
                },
                _name: {
                    as: 'name',
                    type: 'property',
                },
                createdAt: {
                    type: 'property',
                    transformer: new DateToStringTransformer(),
                },
            },
        },
    }
}

ObjectType

Represents an instance transformed into an object or an array of instances transformed into an array of objects.

key type description
type "object" This should always be "object" and should be provided at all times to make distinction between object-types and property-types
constructor ClassConstructor A class constructor to use for reverse transforming objects into entity instances
as string | undefined When defining a child entity in the mapping, this can be used to specify the name of the key on the parent object that it is transformed into
properties Record<string, PropertyMapping | ObjectMapping> An object containing as key the name of the property on the instance, and as value ObjectType or PropertyType mappings
ignoredProperties string[] By default the transformer will throw an Error when you forget to map properties, to prevent any mistakes. When you want a instance property not to be mapped, add the property key in this array.

PropertyType

Represents a property transformed into something else (up to ypu), or an array of properties transformed.

key type description
type "property" This should always be "property" and should be provided at all times to make distinction between object-types and property-types
as string | undefined If you don't want the original property name to be outputted in the object, define an alternate name
transformer PropValueTransformer A class instance implementing PropValueTransformer interface that can be called during the transformations to change the property value into anything you want

Recipes

Child collections with mixed instance types

When transforming a child collection of mixed instance types, for instance a child collection with instances of Features and Colors, don't define it as an object-mapping, but as a property-mapping and write a custom transformer. Like:

export default class ProductAttributeToObjectTransformer
    implements PropValueTransformer<Color | Feature, ColorAsObject | FeatureAsObject>
{
    reverseTransform(to: ColorAsObject | FeatureAsObject): Color | Feature {
        // ...
    }

    transform(from: Color | Feature): ColorAsObject | FeatureAsObject {
        // ...
    }
}

Feel free however to re-use the EntityToObjectTransformer (or any other custom transformer you wrote) inside this transformer, to transform any child objects of some sorts.

Instantiating classes with runtime validation

As the EntityToObjectTranformer will, when reverse transforming, instantiate objects without supplying constructor variables, and there might be some runtime validation there, this might result in errors. To prevent this, for now, you need to write a custom transformer for this and supply this in your mapping.

Known limitations and todos

  • As constructors are instantiated without arguments, and the props are set on the instance, there might be problems with runtime validation in the constructor. It would be nice if we could support this without the user having to write a custom transformer for it.
  • Support arrays that contain different types of instances, without using a custom transformer
  • Be able to use custom child collection classes and loop through them

Package Sidebar

Install

npm i @gnoesiboe/entity-to-object-transformer

Weekly Downloads

1

Version

2.3.0

License

MIT

Unpacked Size

44 kB

Total Files

8

Last publish

Collaborators

  • gnoesiboe