Michal Zalecki
Michal Zalecki
software development, testing, JavaScript,
TypeScript, Node.js, React, and other stuff

Simplifying data with object mappers

We are designing our applications for better separation and many patterns evolved from that effort. It's not any different in case of projects I have done. There were couple architectures along the way and tries to simplify all of the building block no matter whether it was MVC, MVVM, Flux, Redux and so on. The glue for all of those elements is data. At the end of the day everything operates or consumes data. When you make your data model flat and decoupled form API you gain full control over modeling you application state.

Normalizing API communication

There is a high chance you are using camelCase naming convention while your payloads are build with snake_cased keys which became de facto standard in PHP, Ruby or Python. It is not a big trade-off in general but the fact you are mixing camel case and snake case while it is so easy to change is rather neglectful. Letting off in small things brakes the feel of code quality. We should play the role of good scouts and always leave the campground cleaner than we found it.

The other problem with REST APIs is that not all of them are… well thought-out.

resuming_date
start_date
cancelled_at
valid_until
timestamp

That's actually a real example from a friend of mine. They need a new front-end while back-end "was fine", ouch!

Flattening nested responses

Unfortunately it's quite common that responses reflects associations. Nesting is hardly ever what makes your code happy, no matter we are talking about if statements, wast CSS selectors or data structures. Transforming nested object according to some pre-established pattern is also error prone due to the nasty Cannot read property of undefined. Why is it better to work with flat data structure instead of nested one? You may think that you need the dot in your model if you are using Angular and it won't make any difference when you use React which doesn't care. The point is you should be in control where the dot is if any.

Empty string and null

Inputs shouldn't be influenced by the way validation on back-end works. Sounds wrong, isn't it? Let's assume we have an optional field. We can send null or string but validation requires at least three characters. Sending an empty string always results in error but empty form element's value is an empty string. Basically what we should do is to create the possibility of mapping empty strings to nulls. On the other hand we of course would like to avoid introducing a new if statement as each of those introduces branch and so additional test case to write.

Omitting values

When it comes to doing partial update you would like to be able to omit some fields. Maybe because you don't want to send back the id of the resource in the request body or maybe you don't want adding entries with undefined values to the query string. There is plenty of scenarios. Using delete keyword leads to object mutation and as far as I'm concerned that's not what I'd go for.

Mappet

What do you do in JavaScript world when you have a problem? Looking for the right library! When I didn't come across satisfying solution I have made mappet.

const uppercase = text => text.toUpperCase();
const skipNull = (dest, value, modifier) => value !== null;

const schema = [
  // ["path.to.destination", "path.to.source", modifierFn]
  ["firstName", "first_name", uppercase],
  ["age", "age"],
  ["nickname", "nickname"],
  ["cardNumber", "card.number"],
];

const mapper = mappet(schema, skipNull);

const source = {
  first_name: "Michal",
  age: 21,
  nickname: null,
  card: {
    number: "5555-5555-5555-4444",
  },
};

const result = mapper(source);

//  {
//    firstName: "MICHAL",
//    age: 21,
//    cardNumber: "5555-5555-5555-4444"
//  }

Mappet is basically mappers factory which allows you to produce closures which perform various operations based on defined schema. Each entry can have its own modifier function. Omit values can be achieved using per schema filter function. What's really handy is that such mappers are composable and easy to reuse e.g. when both clients and companies have credit card association. I'm writing about it not to force the solution but to make it clear what approach solved my problems so probably it will solve yours.

Photo by Oliver Hale on Unsplash