Helper for simple data transformation from one view to another
npm i -P data-transformer
For example, you have a list of users from your backend
const users = [
{
'user_id': '476a44d6-8082-42b8-aa1e-378feb713169',
'first_name': 'Marilyn',
'second_name': 'Charles',
'contacts': {
'email': 'marilyncharles@apextri.com',
'phone': '+1 (860) 439-2219',
'address': '836 Lewis Place, Camas, Puerto Rico, 7243',
},
},
{
'user_id': '10d25293-9a17-4df0-8045-17ca5f4e8b5f',
'first_name': 'Corrine',
'second_name': 'Huffman',
'contacts': {
'email': 'corrinehuffman@artworlds.com',
'phone': '+1 (812) 534-3924',
'address': '450 Highland Avenue, Ryderwood, Colorado, 7095',
},
},
];And a transformer class to convert data to a flat presentation format
import { Property, Transform } from 'data-transformer';
class User {
@Property('user_id')
id;
@Property({
first: 'first_name',
second: 'second_name'
})
@Transform({
from: ({ value }) => [value.first, value.second].join(' '),
to: ({ value }) => {
const [first, second] = value.split(' ');
return { first, second };
}
})
name;
@Property('contacts.email')
email;
@Property('contacts.phone')
phone;
@Property('contacts.address')
address;
}As a result of transformation
import { dataToModel } from 'data-transformer';
const result = await dataToModel(User, users, { output: Array });you will get the structure
[
{
id: '476a44d6-8082-42b8-aa1e-378feb713169',
name: 'Marilyn Charles',
email: 'marilyncharles@apextri.com',
phone: '+1 (860) 439-2219',
address: '836 Lewis Place, Camas, Puerto Rico, 7243',
},
{
id: '10d25293-9a17-4df0-8045-17ca5f4e8b5f',
name: 'Corrine Huffman',
email: 'corrinehuffman@artworlds.com',
phone: '+1 (812) 534-3924',
address: '450 Highland Avenue, Ryderwood, Colorado, 7095',
},
];Also, as a result of reverse transformation
import { modelToData } from 'data-transformer';
const source = await modelToData(User, result);you will get a new source structure in the same presentation as the original users structure.
Note. Transformer has no internal state, therefore, he doesn't remember the initial data of the initial transformation. During reverse transformation, to obtain the initial data, all initial properties should be declared in the transformer class.
Each transformer class property decorate via micro-transformers(decorators) each of which, returns a new property value.
The transformer has some important features that you should pay attention to:
- Transformer has no internal state. If as a result of reverse transformation you want to get an object, identical to the initial one - in the class of the transformer is necessary to declare all the necessary properties.
- One property can have an unlimited number of decorators. Each of which, in the order of declaration, from the top to the bottom, one by one will transform the value.
If declared, always the first. Takes property value by given key.
Available key type is string, object, array.
If this decorator is not used, the property will be virtual, watch the decorator @Transform
const data = {
user_id: 123,
first_name: 'Corrine',
second_name: 'Huffman',
contacts: {
phone: '+1 (812) 534-3924',
address: '450 Highland Avenue, Ryderwood, Colorado, 7095',
},
docSerial: 'ZZ',
docNumber: '123456'
};import { dataToModel, Property } from 'data-transformer';
class User {
@Property('user_id')
id;
@Property('contacts.phone')
phone;
@Property({
first: 'first_name',
second: 'second_name',
})
name;
@Property(['docSerial', 'docNumber'])
passport;
}
const user = await dataToModel(User, data);user result is:
{
id: 123,
phone: '+1 (812) 534-3924',
name: {
first: 'Corrine',
second: 'Huffman',
},
passport: ['ZZ', '123456'],
}Return value from map.
const data = {
role: '1',
};import { dataToModel, Property, Enum } from 'data-transformer';
class User {
@Property('role')
@Enum({
1: 'DEVELOPER',
2: 'USER'
})
role;
}
const user = await dataToModel(User, data);user result is:
{
role: 'DEVELOPER',
}Transform from one format to another one. Under the hood is used Moment.js.
const data = {
create_date: 1529442000,
};import { dataToModel, Property, Date } from 'data-transformer';
class User {
@Property('create_date')
@Date({
from: 'X',
to: 'DD.MM.YYYY'
})
date;
}
const user = await dataToModel(User, data);user result is:
{
date: '20.06.2018',
}Transform initial value to required format or transform nested objects
const data = {
user_id: 123,
is_active: null,
age: '24',
contacts: {
phone: '+1 (812) 534-3924',
},
friends: [
{
first_name: 'Iris',
second_name: 'Sexton',
},
{
first_name: 'Ava',
second_name: 'Wilcox'
}
]
};import { dataToModel, Property, Type } from 'data-transformer';
class Contact {
@Property('phone')
phone;
}
class Friend {
@Property({
first: 'first_name',
second: 'second_name'
})
@Transform({
from: ({ value }) => [value.first, value.second].join(' '),
to: ({ value }) => {
const [first, second] = value.split(' ');
return { first, second };
}
})
name;
}
class User {
@Property('user_id')
@Type(String)
id;
@Property('is_active')
@Type(Boolean)
isActive;
@Property('contacts')
@Type(Contact)
contacts;
@Property('friends')
@Type(Friend, Array)
friends;
}
const user = await dataToModel(User, data);user result is:
{
id: '20.06.2018',
isActive: false,
contacts: {
phone: '+1 (812) 534-3924',
},
friends: [
{
name:'Iris Sexton',
},
{
name:'Ava Wilcox',
},
]
}Used for custom value transformations. For transformations you should declare one or both optional methods,
from for transformation your data to model representation and to for reverse transformation.
Both methods, from and to, receive one argument object with four parameters:
- value - value, obtained by key(-s) from @Property
- source - initial data
- target - transformed data at the time of transformation of a given value
- meta - helper, custom object which you can declare in a method dataToModel
As result methods should return value.
const data = {
residency: false,
internal: true,
firstName: 'Ava',
secondName: 'Wilcox',
document: 'ZZ_123456 '
};import { dataToModel, Property, Transform } from 'data-transformer';
class User {
@Property('document')
@Transform({
from: ({ value, source, target, meta }) => value.replace(/[\s_]/g, ''),
to: ({ value, source, target, meta }) => value.replace(/(\D)(\d)/, '$1_$2'),
})
document;
@Transform({
from: ({ value, source, target, meta }) => !source.residency && source.internal,
})
nonresident;
@Transform({
from: ({ value, source, target, meta }) => [source.firstName, source.secondName].join(' '),
})
name;
}
const user = await dataToModel(User, data);user result is:
{
document: 'ZZ123456',
nonresident: true,
name:'Ava Wilcox'
}Note. After reverse transformation the result will be as follows.
Cause properties without @Property decorator - are virtual.
{
document: 'ZZ_123456',
}Used for top-level data transformations. For transformations you should declare one or both optional methods from, to.
Method from is called before all properties transformations.
Method to is called after all properties transformations.
Both methods, from and to, receive one argument object with two parameters:
- value - initial data
- meta - helper, custom object which you can declare in a method dataToModel
As result methods should return value.
const data = [
{
price: '200.20',
},
{
price: '100',
},
{
price: '98.56',
},
];import { dataToModel, Property, DataTransform, Type } from 'data-transformer';
@DataTransform({
from: ({ value }) => value.filter(({ price }) => price >= 100)
})
class Product {
@Property('price')
@Type(Number)
price;
}
const products = await dataToModel(Product, data, { output: Array });products result is:
[
{
price: 200.20,
},
{
price: 100,
},
];Both methods are asynchronous.
dataToModel transforms the initial data according to the transformer class.
modelToData does the reverse transformation.
Both methods еakes three arguments:
- class transformer, is required
- initial data, is required
- optional parameters, is optional
- output - is optional, accept only one value -
Array. Necessary if the initial data is an array. - meta - helper, custom object which will be available in methods
fromandtodecorators@Transformand@DataTransform.
- output - is optional, accept only one value -
import { dataToModel, modelToData } from 'data-transformer';
class Transformer {
...
}
const target = await dataToModel(Transformer, data, { output: Array, meta: { ... } });
const source = await modelToData(Transformer, target, { output: Array, meta: { ... } });Synchronous method for finding abstract path. Correctly work only with @Property(key is string) and @Type decorators
import { findPath, Property, Type } from 'data-transformer';
class Friend {
@Property('first_name')
firstName;
@Property('second_name')
secondName;
}
class User {
@Property('friends_list')
@Type(Friend, Array)
friends;
}
const path = findPath(User, 'friends_list[0].first_name');path result is:
'friends[0].firstName'Method that allows you to create your custom decorator, with your custom logic.
createDecorator
import { createDecorator } from 'data-transformer';
export const Custom = createDecorator((option1, option2, optionN) => ({
from({ value, target, source, meta }) {
return value;
},
to({ value, target, source, meta }) {
return value;
},
}));
class Transform {
@Custom(option1, option2, optionN)
key;
}Transformer classes can be inherited.
const data = {
first_name: 'John',
second_name: 'Doe',
role: '1',
last_modify: '20.11.2019',
};import { Property, Date } from 'data-transformer';
class User {
@Property({
first: 'first_name',
second: 'second_name',
})
@Transform({
from: ({ value }) => [value.first, value.second].join(' '),
to: ({ value }) => {
const [first, second] = value.split(' ');
return { first, second };
},
})
name;
@Property('last_modify')
@Date({
from: 'DD.MM.YYYY',
to: 'X',
})
lastModify;
}
class Employee extends User {
@Property('role')
@Enum({
1: 'ADMIN',
})
role;
}
const user = await dataToModel(Employee, data);user result is:
{
name: 'John Doe',
lastModify: 1574200800,
role: 'ADMIN',
}