Gathering detailed insights and metrics for morphism
Gathering detailed insights and metrics for morphism
Gathering detailed insights and metrics for morphism
Gathering detailed insights and metrics for morphism
⚡ Type-safe data transformer for JavaScript, TypeScript & Node.js.
npm install morphism
Typescript
Module System
Node Version
NPM Version
96.7
Supply Chain
100
Quality
75.9
Maintenance
100
Vulnerability
100
License
TypeScript (99.53%)
JavaScript (0.47%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
492 Stars
789 Commits
23 Forks
10 Watchers
22 Branches
9 Contributors
Updated on Jun 25, 2025
Latest Version
1.12.3
Package Id
morphism@1.12.3
Unpacked Size
88.37 kB
Size
24.11 kB
File Count
12
NPM Version
6.5.0
Node Version
9.11.2
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
27
In many fields of mathematics, morphism refers to a structure-preserving map from one mathematical structure to another. A morphism f with source X and target Y is written f : X → Y. Thus a morphism is represented by an arrow from its source to its target.
https://en.wikipedia.org/wiki/Morphism
1npm install --save morphism
or in the browser
1<script src="https://unpkg.com/morphism/dist/morphism.js"></script> 2<script> 3 const { morphism, createSchema } = Morphism 4</script>
The entry point of a morphism is the schema. The keys
represent the shape of your target object, and the values
represents one of the several ways to access the properties of the incoming source.
1const schema = { 2 targetProperty: 'sourceProperty' 3};
Then use the morphism
function along with the schema to transform any source to your desired target
1import { morphism } from 'morphism'; 2 3const source = { 4 _firstName: 'Mirza' 5}; 6 7const schema = { 8 name: '_firstName' 9}; 10 11morphism(schema, source); 12➡ 13{ 14 "name": "Mirza" 15}
You may specify properties deep within the source object to be copied to your desired target by using dot notation in the mapping value
.
This is one of the actions available to transform the source data
1const schema = { 2 foo: 'deep.foo', 3 bar: { 4 baz: 'deep.foo' 5 } 6}; 7 8const source = { 9 deep: { 10 foo: 'value' 11 } 12}; 13 14morphism(schema, source); 15➡ 16{ 17 "foo": "value", 18 "bar": { 19 "baz": "value" 20 } 21}
One important rule of Morphism
is that it will always return a result respecting the dimension of the source data. If the source data is an array
, morphism will outputs an array
, if the source data is an object
you'll have an object
1const schema = { 2 foo: 'bar' 3}; 4 5// The source is a single object 6const object = { 7 bar: 'value' 8}; 9 10morphism(schema, object); 11➡ 12{ 13 "foo": "value" 14} 15 16// The source is a collection of objects 17const multipleObjects = [{ 18 bar: 'value' 19}]; 20 21morphism(schema, multipleObjects); 22➡ 23[{ 24 "foo": "value" 25}]
1import { morphism, StrictSchema } from 'morphism'; 2 3// What we have 4interface Source { 5 ugly_field: string; 6} 7 8// What we want 9interface Destination { 10 field: string; 11} 12 13const source: Source = { 14 ugly_field: 'field value' 15}; 16 17// Destination and Source types are optional 18morphism<StrictSchema<Destination, Source>>({ field: 'ugly_field' }, source); 19// => {field: "field value"} 20 21// Or 22const sources = [source]; 23const schema: StrictSchema<Destination, Source> = { field: 'ugly_field' }; 24morphism(schema, sources); 25// => [{field: "field value"}]
We live in a era where we deal with mutiple data contracts coming from several sources (Rest API, Services, Raw JSON...). When it comes to transform multiple data contracts to match with your domain objects, it's common to create your objects with Object.assign
, new Object(sourceProperty1, sourceProperty2)
or by simply assigning each source properties to your destination. This can result in your business logic being spread all over the place.
Morphism
allows you to keep this business logic centralized and brings you a top-down view of your data transformation. When a contract change occurs, it helps to track the bug since you just need to refer to your schema
When you type your schema, this library will require you to specify each transformation for your required fields.
This library uses TypeScript extensively. The target type will be inferred from the defined schema.
When using an ActionFunction
the input type is also inferred to enforce your transformations
See below the different options you have for the schema.
Morphism
comes with 3 artifacts to achieve your transformations:
A schema is an object-preserving map from one data structure to another.
The keys of the schema match the desired destination structure. Each value corresponds to an Action applied by Morphism when iterating over the input data.
You can use 4 kind of values for the keys of your schema:
ActionString
: A string that allows to perform a projection from a propertyActionSelector
: An Object that allows to perform a function over a source property's valueActionFunction
: A Function that allows to perform a function over source propertyActionAggregator
: An Array of Strings that allows to perform a function over source property1import { morphism } from 'morphism'; 2 3const input = { 4 foo: { 5 baz: 'value1' 6 } 7}; 8 9const schema = { 10 bar: 'foo', // ActionString: Allows to perform a projection from a property 11 qux: ['foo', 'foo.baz'], // ActionAggregator: Allows to aggregate multiple properties 12 quux: (iteratee, source, destination) => { 13 // ActionFunction: Allows to perform a function over source property 14 return iteratee.foo; 15 }, 16 corge: { 17 // ActionSelector: Allows to perform a function over a source property's value 18 path: 'foo.baz', 19 fn: (propertyValue, source) => { 20 return propertyValue; 21 } 22 } 23}; 24 25morphism(schema, input); 26// { 27// "bar": { 28// "baz": "value1" 29// }, 30// "qux": { 31// "foo": { 32// "baz": "value1" 33// } 34// }, 35// "quux": { 36// "baz": "value1" 37// }, 38// "corge": "value1" 39// }
You might want to enforce the keys provided in your schema using Typescript
. This is possible using a StrictSchema
. Doing so will require to map every field of the Target
type provided.
1interface IFoo { 2 foo: string; 3 bar: number; 4} 5const schema: StrictSchema<IFoo> = { foo: 'qux', bar: () => 'test' }; 6const source = { qux: 'foo' }; 7const target = morphism(schema, source); 8// { 9// "foo": "qux", 10// "bar": "test" 11// }
The simplest way to use morphism is to import the currying function:
1import { morphism } from 'morphism';
morphism
either outputs a mapping function or the transformed data depending on the usage:
1morphism(schema: Schema, items?: any, type?: any): any
1// Outputs a function when only a schema is provided 2const fn = morphism(schema); 3const result = fn(data); 4 5// Outputs the transformed data when a schema and the source data are provided 6const result = morphism(schema, data); 7 8// Outputs the transformed data as an ES6 Class Object when a schema, the source data and an ES6 Class are provided 9const result = morphism(schema, data, Foo); 10// => Items in result are instance of Foo
You can also use Function Decorators on your method or functions to transform the return value using Morphism
:
toJsObject
Decorator1import { toJSObject } from 'morphism'; 2 3class Service { 4 @toJSObject({ 5 foo: currentItem => currentItem.foo, 6 baz: 'bar.baz' 7 }) 8 async fetch() { 9 const response = await fetch('https://api.com'); 10 return response.json(); 11 // => 12 // { 13 // foo: 'fooValue' 14 // bar: { 15 // baz: 'bazValue' 16 // } 17 // }; 18 } 19} 20 21// await service.fetch() will return 22// => 23// { 24// foo: 'fooValue', 25// baz: 'bazValue' 26// } 27 28-------------------------------- 29 30// Using Typescript will enforce the key from the target to be required 31class Target { 32 a: string = null; 33 b: string = null; 34} 35class Service { 36 // By Using <Target>, Mapping for Properties `a` and `b` will be required 37 @toJSObject<Target>({ 38 a: currentItem => currentItem.foo, 39 b: 'bar.baz' 40 }) 41 fetch(); 42}
toClassObject
Decorator1import { toClassObject } from 'morphism'; 2 3class Target { 4 foo = null; 5 bar = null; 6} 7const schema = { 8 foo: currentItem => currentItem.foo, 9 baz: 'bar.baz' 10}; 11class Service { 12 @toClassObject(schema, Target) 13 async fetch() { 14 const response = await fetch('https://api.com'); 15 return response.json(); 16 // => 17 // { 18 // foo: 'fooValue' 19 // bar: { 20 // baz: 'bazValue' 21 // } 22 // }; 23 } 24} 25 26// await service.fetch() will be instanceof Target 27// => 28// Target { 29// foo: 'fooValue', 30// baz: 'bazValue' 31// }
morph
DecoratorUtility decorator wrapping toClassObject
and toJSObject
decorators
1import { toClassObject } from 'morphism'; 2 3class Target { 4 foo = null; 5 bar = null; 6} 7const schema = { 8 foo: currentItem => currentItem.foo, 9 baz: 'bar.baz' 10}; 11class Service { 12 @morph(schema) 13 async fetch() { 14 const response = await fetch('https://api.com'); 15 return response.json(); 16 // => 17 // { 18 // foo: 'fooValue' 19 // bar: { 20 // baz: 'bazValue' 21 // } 22 // }; 23 } 24 @morph(schema, Target) 25 async fetch2() { 26 const response = await fetch('https://api.com'); 27 return response.json(); 28 } 29} 30// await service.fetch() will be 31// => 32// { 33// foo: 'fooValue', 34// baz: 'bazValue' 35// } 36 37// await service.fetch() will be instanceof Target 38// => 39// Target { 40// foo: 'fooValue', 41// baz: 'bazValue' 42// }
📚 Morphism Function as Decorators Docs
Morphism comes along with an internal registry you can use to save your schema attached to a specific ES6 Class.
In order to use the registry, you might want to use the default export:
1import Morphism from 'morphism';
All features available with the currying function are also available when using the plain object plus the internal registry:
1// Currying Function 2Morphism(schema: Schema, items?: any, type?: any): any 3 4// Registry API 5Morphism.register(type: any, schema?: Schema); 6Morphism.map(type: any, data?: any); 7Morphism.setMapper(type: any, schema: Schema); 8Morphism.getMapper(type); 9Morphism.deleteMapper(type); 10Morphism.mappers
1import { morphism } from 'morphism'; 2// Source data coming from an API. 3const source = { 4 foo: 'baz', 5 bar: ['bar', 'foo'], 6 baz: { 7 qux: 'bazqux' 8 } 9}; 10const schema = { 11 foo: 'foo', // Simple Projection 12 bazqux: 'baz.qux' // Grab a value from a deep path 13}; 14 15morphism(schema, source); 16//=> { foo: 'baz', bazqux: 'bazqux' }
1import { morphism } from 'morphism'; 2// Source data coming from an API. 3const source = { 4 foo: { 5 bar: 'bar' 6 } 7}; 8let schema = { 9 barqux: { 10 path: 'foo.bar', 11 fn: value => `${value}qux` // Apply a function over the source property's value 12 } 13}; 14 15morphism(schema, source); 16//=> { barqux: 'barqux' }
1import { morphism } from 'morphism'; 2// Source data coming from an API. 3const source = { 4 foo: { 5 bar: 'bar' 6 } 7}; 8let schema = { 9 bar: iteratee => { 10 // Apply a function over the source propery 11 return iteratee.foo.bar; 12 } 13}; 14 15morphism(schema, source); 16//=> { bar: 'bar' }
1import { morphism } from 'morphism'; 2// Source data coming from an API. 3const source = { 4 foo: 'foo', 5 bar: 'bar' 6}; 7let schema = { 8 fooAndBar: ['foo', 'bar'] // Grab these properties into fooAndBar 9}; 10 11morphism(schema, source); 12//=> { fooAndBar: { foo: 'foo', bar: 'bar' } }
Register a mapper for a specific type. The schema is optional.
1Morphism.register(type: any, schema?: Schema);
Map a collection of objects to the specified type
1Morphism.map(type: any, data?: any);
1Morphism.setMapper(type: any, schema: Schema);
2Morphism.getMapper(type);
1Morphism.deleteMapper(type);
1Morphism.mappers;
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
MIT © Yann Renaudin
No vulnerabilities found.
Reason
all changesets reviewed
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
84 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More