Unleash Object-Oriented Programming When Consuming APIs

Suppose we are building a Single Page Application and have the following classes (or models in a Angular app):

// user.ts
import { Profile } from './profile';

export class User {
  id: number;
  first_name: string;
  last_name: string;
  profile: Profile;

  fullName() {
    return `${this.first_name} ${this.last_name}`
  }
}
// profile.ts
export class Profile {
  id: number;
  score: number;

  goodScore() {
    return this.score > 100
  }
}

Now, you gotta consume the user api, which returns the following json:

response = `{
  "id": 1,
  "first_name": "Elon",
  "last_name": "Musk",
  "profile": {
    "id": 10,
    "score": 200
  }
}`

As soon as you parse your response json, you will get a literal object:

parsed_json = JSON.parse(response)
parsed_json.constructor.name
=> "Object"

The problem is.. A literal object is not a class object, you will not be able to do things like:

user.fullName()
user.profile.goodScore()

We want to use the full power of object-orientation in JavaScript. How can we pull it off?

Enter class-transformer

class-transformer is a small npm package that does the heavy lifting for us.

import { plainToClass } from "class-transformer";

user_instance = plainToClass(User, response)
user_instance.constructor.name
=> "User"

plainToClass converts a literal object into a class object.

Now we are free to use our methods:

user_instance.fullName()
=> "Elon Musk"

But.. If we try to access a nested object like profile, it will not work:

user_instance.profile.goodScore()
=> profile.goodScore is not a function

That is because the library does not know which class must be used, so we need to manually define the type (line 9):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// user.ts
import { Profile } from './profile';
import { Type } from "class-transformer";

export class User {
  id: number;
  first_name: string;
  last_name: string;
  @Type(() => Profile)
  profile: Profile;

  fullName() {
    return `${this.first_name} ${this.last_name}`
  }
}

Ok, now we are good to go:

user_instance.profile.goodScore()
=> true

This library has many more useful functions, I don't want to explain their whole README here, so, go check it out.

Written on January 16, 2019

Share: