{"id":15659413,"url":"https://github.com/nartc/automapper-nartc","last_synced_at":"2025-04-30T16:07:40.381Z","repository":{"id":35010529,"uuid":"196241119","full_name":"nartc/automapper-nartc","owner":"nartc","description":"(Deprecated) An automapper-ts fork (by Bert Loedeman)","archived":false,"fork":false,"pushed_at":"2023-01-04T21:55:15.000Z","size":1107,"stargazers_count":21,"open_issues_count":21,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-18T04:42:48.464Z","etag":null,"topics":["automapper","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://nartc.github.io/automapper-nartc/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nartc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-10T16:37:12.000Z","updated_at":"2023-10-13T15:07:06.000Z","dependencies_parsed_at":"2023-01-15T11:56:56.093Z","dependency_job_id":null,"html_url":"https://github.com/nartc/automapper-nartc","commit_stats":null,"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fautomapper-nartc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fautomapper-nartc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fautomapper-nartc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nartc%2Fautomapper-nartc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nartc","download_url":"https://codeload.github.com/nartc/automapper-nartc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251319359,"owners_count":21570420,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automapper","nodejs","typescript"],"created_at":"2024-10-03T13:16:47.393Z","updated_at":"2025-04-30T16:07:40.326Z","avatar_url":"https://github.com/nartc.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Deprecated Warning: \n#### This package is no longer maintained as I just refactored the whole mapping logic and moved to a new package: [@nartc/automapper](https://github.com/nartc/mapper). You can still use this `automapper-nartc` but it will no longer be maintained and updated. \n\n\n# AutoMapper - Nartc\n\nThis is a fork of `automapper-ts` by [Bert Loedeman](https://github.com/loedeman). My goal is to re-create this awesome library with a more strong-type approach while learning `TypeScript` myself.\n\n## Documentations\n\nGithub Pages\n[https://nartc.github.io/automapper-nartc/](https://nartc.github.io/automapper-nartc/)\n\n## Motivations\n\nI know that `AutoMapper` is pretty weak in `TypeScript` because of how `Reflection` works in `TypeScript`. However, it'd be nice to have some type of `Mapper` that works for `NodeJS` development.\n\n## Features\n\nFeatures are limited since I am, by no mean, a `TypeScript` nor an `AutoMapper` expert which I'm planning to research more to provide more `AutoMapper` features to this library.\n\nSo far, the following is supported:\n\n- [x] Basic Mapping between two classes\n- [x] Basic Mapping for nested classes\n- [x] Array/List Mapping\n- [x] Flattening\n- [x] ReverseMap - Very basic `reverseMapping` feature. Use for primitives models only if you can.\n- [x] Value Converters\n- [x] Value Resolvers\n- [x] Async\n- [x] Before/After Callback\n- [x] Naming Conventions\n\n**NOTE: Please be advised that the current state of this library is for learning purposes and I'd appreciate any help/guides. Everything is still in beta and DO NOT USE in production.**\n\n#### Future features:\n\n- [ ] Type Converters - Help needed\n- [ ] Value Transformers\n\n#### Will not support:\n\n- [x] Null Substitution - It makes more sense to use `fromValue()` instead of implement `nullSubstitution()`. Please let me know of a use-case where `nullSubstitution()` makes sense.\n\nContributions are appreciated.\n\n#### Implementation note:\n\nI have plans in the near future to update how `forMember()` method works in terms of the method's signature. I might change it to a lambda expression to support `reverseMapping` better. But I am open to suggestions.\n\n## Installation\n\n```shell\nnpm install --save automapper-nartc\n```\n\n**NOTE: `automapper-nartc` has a `peerDependency` of `reflect-metadata` and a dependency of `class-transformer`. `class-transformer` will also be installed when you install this library. Please also turn on `experimentalDecorators` and `emitDecoratorMetadata` in your `tsconfig` **\n\n## Usage\n\n1. Assuming you have couple of `Domain Models` as follows:\n\n```typescript\nclass Address {\n  address: string;\n  city: string;\n  state: string;\n  zip: string;\n}\n\nclass Profile {\n  bio: string;\n  phone: string;\n  email: string;\n  addresses: Address[];\n\n  constructor() {\n    this.addresses = [];\n  }\n}\n\nclass User {\n  firstName: string;\n  lastName: string;\n  password: string;\n  profile: Profile;\n}\n```\n\n2. And you also have couple of `View Models` (or `DTOs`):\n\n```typescript\nclass ProfileVm {\n  bio: string;\n  email: string;\n  addressStrings: string[];\n}\n\nclass UserVm {\n  fullName: string;\n  profile: ProfileVm;\n  firstName?: string;\n  lastName?: string;\n}\n```\n\n3. Decorate all of your properties with `@Expose()`. `@Expose` is imported from `class-transformer`. This will allow the engine to be aware of all the properties available in a certain **class**.\n\n```typescript\nclass User {\n  @Expose()\n  firstName: string;\n  @Expose()\n  lastName: string;\n  @Expose()\n  password: string;\n  @Expose()\n  profile: Profile;\n}\n\nclass UserVm {\n  @Expose()\n  fullName: string;\n  @Expose()\n  profile: ProfileVm;\n  @Expose()\n  firstName?: string;\n  @Expose()\n  lastName?: string;\n}\n```\n\n**NOTE: If you have nested model, like `profile` in this case, you will want to use `@Type()` on those as well. `@Type()` is also imported from `class-transformer`.**\n\n```typescript\nclass User {\n  @Expose()\n  firstName: string;\n  @Expose()\n  lastName: string;\n  @Expose()\n  password: string;\n  @Expose()\n  @Type(() =\u003e Profile)\n  profile: Profile;\n}\n\nclass UserVm {\n  @Expose()\n  fullName: string;\n  @Expose()\n  @Type(() =\u003e ProfileVm)\n  profile: ProfileVm;\n  @Expose()\n  firstName?: string;\n  @Expose()\n  lastName?: string;\n}\n```\n\nHowever, `automapper-nartc` provides a short-hand decorator `@ExposedType()` instead of explicitly use `@Expose()` and `@Type()` on a nested model property.\n\n```typescript\nclass UserVm {\n  @Expose()\n  fullName: string;\n  @ExposedType(() =\u003e ProfileVm)\n  profile: ProfileVm;\n  @Expose()\n  firstName?: string;\n  @Expose()\n  lastName?: string;\n}\n```\n\n4. Next, import `Mapper` from `automapper-nartc`. You can also just instantiate a new instance of `AutoMapper` if you want to manage your instance.\n5. Initialize `Mapper` with `initialize()` method. `initialize()` expects a `Configuration` callback that will give you access to the `Configuration` object. There are two methods on the `Configuration` object that you can use to setup your `Mapper`\n\n- `createMap()`: `createMap()` expects a **source** as the first argument and the **destination** as the second argument. `createMap()` returns `CreateMapFluentFunctions\u003cTSource, TDestination\u003e` (Read more at [API Reference](https://nartc.github.io/automapper-nartc/index.html)).\n\n```typescript\nimport { Mapper, MappingProfileBase } from 'automapper-nartc';\n\nMapper.initialize(config =\u003e {\n  config.createMap(User, UserVm); // create a mapping from User to UserVm (one direction)\n  config.createMap(Profile, ProfileVm)\n    .forMember('addressStrings', opts =\u003e opts.mapFrom(s =\u003e s.addresses.map(... /* map to addressString however you like */)));\n});\n```\n\n`createMap()` will establish basic mappings for: `primitives` and `nested mapping` that have the same field name on the **source** and **destination** (eg: `userVm.firstName` will be automatically mapped from `user.firstName`). In addition, you can use `forMember()` to gain more control on how to map a field on the **destination**.\n\n```typescript\nMapper.initialize(config =\u003e {\n  config\n    .createMap(User, UserVm) // create a mapping from User to UserVm (one direction)\n    .forMember('fullName', opts =\u003e\n      opts.mapFrom(source =\u003e source.firstName + ' ' + source.lastName)\n    ); // You will get type-inference here\n});\n```\n\n- `addProfile()`: `addProfile()` expects a new instance of a class which extends `MappingProfileBase`. Usually, you can just initialize your `Mapper` with `config.createMap` and setup all your mappings that way. But more than often, it is better to separate your mappings into `Profile` which will create the mappings for specific set of **source** and **destination**\n\n```typescript\nimport { MappingProfileBase } from 'automapper-nartc';\n\nexport class UserProfile extends MappingProfileBase {\n  constructor() {\n    super(); // this is required since it will take UserProfile and get the string \"UserProfile\" to assign to profileName\n  }\n\n  // configure() is required since it is an abstract method. configure() will be called automatically by Mapper.\n  // This is where you will setup your mapping with the class method: createMap\n  configure(mapper: AutoMapper) {\n    mapper\n      .createMap(User, UserVm)\n      .forMember('fullName', opts =\u003e\n        opts.mapFrom(source =\u003e source.firstName + ' ' + source.lastName)\n      ); // You will get type-inference here\n  }\n}\n\n// in another file\nMapper.initialize(config =\u003e {\n  config.addProfile(new UserProfile());\n});\n```\n\n5. When you're ready to map, call `Mapper.map()`.\n\n```typescript\nconst userVm = Mapper.map(user, UserVm); // this will return an instance of UserVm and assign it to userVm with all the fields assigned properly from User\n\nconsole.log('instance of UserVm?', userVm instanceof UserVm); // true\n```\n\n#### Callbacks\n\n`automapper-nartc` provides `beforeMap` and `afterMap` callbacks which are called **before** a mapping operator occurs and/or **after** a mapping operator occurs, if said callbacks are provided.\n\nThere are two ways you can provide the callbacks: `Map` level and `Mapping` level.\n\n**NOTE: `Map` level refers to the actual map operation when any of the `map()` methods are called. `Mapping` level refers to the actual `Mapping` between two models when `createMap()` is called.**\n\n- **Map** level: all `map()` methods have the third parameter which has a shape of `MapActionOptions: {beforeMap: Function, afterMap: Function}`. If any of the callbacks is provided, it will be called in correct chronological order.\n\n```typescript\n/**\n * In this case, both callbacks will be called with the following arguments.\n *\n * @param {User} source\n * @param {UserVm} destination\n * @param {Mapping\u003cUser, UserVm\u003e} mapping\n */\nconst userVm = Mapper.map(user, UserVm, {\n  beforeMap: (source, destination, mapping) =\u003e {},\n  afterMap: (source, destination, mapping) =\u003e {}\n});\n```\n\n- **Mapping** level: callbacks on the `Mapping` level will be called for ALL map operations on the two models unless you provide diferent callbacks to specific `map` operation (aka `Map` level)\n\n```typescript\n/**\n * In this case, both callbacks will be called with the following arguments.\n *\n * @param {User} source\n * @param {UserVm} destination\n * @param {Mapping\u003cUser, UserVm\u003e} mapping\n */\nMapper.initialize(config =\u003e {\n  config\n    .createMap(User, UserVm)\n    .beforeMap((source, destination, mapping) =\u003e {})\n    .afterMap((source, destination, mapping) =\u003e {}); // create a mapping from User to UserVm (one direction)\n});\n```\n\n**NOTE 1: `Map` level callbacks will overide `Mapping` level callbacks if both are provided**\n\n**NOTE 2: The callbacks are called with `source`, `destination` and `mapping`. **ANYTHING** you do to the `source` and `destination` will be carried over to the `source` and `destination` being mapped (mutation) so please be cautious. It might be handy/dangerous at the same time given the dynamic characteristic of **JavaScript**.**\n\n**NOTE 3: `mapArray()` will ignore `Mapping` level callbacks because that would be a performance issue if callbacks were to be called on every single item in an array. Provide `Map` level callbacks for `mapArray()` if you want to have callbacks on `mapArray()`**\n\n6. Use `Mapper.mapArray()` if you want to map from `TSource[]` to `TDestination[]`.\n\n## Demo\n\nCodesandbox Demo\n[Codesandbox](https://codesandbox.io/s/automapper-nartc-example-l96nw)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnartc%2Fautomapper-nartc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnartc%2Fautomapper-nartc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnartc%2Fautomapper-nartc/lists"}