Mapstronaut: A Flexible Object-Mapping Library for JS/TS



This content originally appeared on DEV Community and was authored by Jonathan P.

Hey everyone!

I’m a senior developer with nearly 20 years of experience, but I’m pretty new to the open-source world. I wanted to share the story of my first major library.

Why Mapstronaut?

After 5 years of intensive development in TypeScript, I switched jobs about a year ago and found myself deep in the Java ecosystem. I had my preconceptions, but I have to admit: with modern frameworks and libraries, the developer experience is excellent.

One library, in particular, became a daily tool for me: MapStruct. It’s a fantastic code generator for mapping one object to another. When I found myself working on Node.js or React projects again, I really missed its power and simplicity. I looked for an open-source equivalent in the JavaScript/TS world, but nothing quite fit my needs.

So, I decided to build it myself.

Introducing Mapstronaut 👨‍🚀

Mapstronaut is a powerful and flexible object mapping library for JavaScript and TypeScript.

Here are some of its key features:

  • Simple, easy-to-understand declarative mapping with transformers, filters, and error handling.
  • Advanced source/target property selectors using JSONPath and dot-prop.
  • Auto-mapping available: maps values present in both the source and target without explicit declaration.
  • Can perform asynchronous transformations and mappings in parallel.
  • Very high test coverage.
  • Comprehensive documentation.

How it works

Let’s start with a basic mapping. Imagine you have a complex source object and need to create a simpler DTO.

const source = {
  spacecraft: {
    name: "Apollo 11",
    crew: ["Neil Armstrong", "Buzz Aldrin", "Michael Collins"],
  },
  mission: {
    year: 1969,
    destination: "Moon",
  },
};

const structure = [
  { source: "spacecraft.name", target: "vesselName" },
  { source: "mission.destination", target: "target" },
  { source: "spacecraft.crew[0]", target: "firstManOnTheMoon" },
];

const result = mapObject(structure, source);

/*
result is:
{
  "vesselName": "Apollo 11",
  "target": "Moon",
  "firstManOnTheMoon": "Neil Armstrong"
}
*/

But Mapstronaut can do much more. Here is an example of a more advanced set of rules:

const structure = [
  {
    source: "mission.budget",
    target: "budgetBillions",
    // Use a custom function to transform values
    transform: (budget) => Math.round((budget / 1e9) * 10) / 10,
  },
  // Use JSONPath to slice an array
  { source: "crew[0:2]", target: "crew.firstTwo" },
  // Use JSONPath to filter an array
  { source: "crew[?(@.age >= 30)]", target: "crew.senior" },
  // Add a constant value to the output
  { constant: "NASA", target: "agency" }
];

Beyond these examples, Mapstronaut is also fully async-ready. This allows you to perform complex operations, like fetching data from an API in the middle of a transformation. All async operations can be run in parallel for maximum performance. You can find detailed examples for this and more in the documentation.

The project is open source under the MIT license.

You can check out the code and documentation here:

https://github.com/jprevo/mapstronaut

I’d love to hear your feedback. What do you use for object mapping in your projects? Are there any features you’d find useful?

Feel free to open an issue or a PR on GitHub.

Thanks for reading!


This content originally appeared on DEV Community and was authored by Jonathan P.