Skip to content

Quickstart

Pieter Verschaffelt edited this page Apr 21, 2019 · 7 revisions

This guide quickly explains the concepts required to get started with this package and automatically consume API's. If you want a more thorough, in-depth explanation, follow Home.

Example API

We will be querying an example API that returns blog posts, categories and users. Each of these items constitute a new Entity. The JSON-format returned for each of these entities, is shown below:

Post

{
  "id": 1,
  "title": "Example post",
  "body": "Lorem ipsum dolor sit amet.",
  "categories": [
    {
      "id": 1,
      "name": "News"
    },
    {
      "id": 2,
      "name": "General"
    }]
}

Category

{
  "id": 3,
  "name": "Example"
}

User

{
  "id": 1,
  "first_name": "John",
  "last_name": "Doe",
  "email": "john.doe@example.com"
}

Entities

We will be creating a new class for each of these items. Every class needs to inherit from Entity, as these items form information that we will be retrieving from our API later on.

Post

import {Entity} from "typescript-rest-mapper";

export default class Post extends Entity {
  private _title: string;
  private _body: string;
  private _categories: Category[];
  
  public get title(): string {
    return this._title;
  }

  public set title(title: string) {
    this._title = title;
  }

  public get body(): string {
    return this._body;
  }

  public set body(body: string) {
    this._body = body;
  }

  public get categories(): Category[] {
    return this._categories;
  }

  public set categories(categories: Category[]) {
    this._categories = categories;
  }
}

Category

import {Entity} from "typescript-rest-mapper";

export default class Category extends Entity {
  private _name: string;

  public get name(): string {
    return this._name;
  }

  public set name(name: string) {
    this._name = name;
  }
}

User

import {Entity} from "typescript-rest-mapper";

export default class User extends Entity {
  private _firstName: string;
  private _lastName: string;
  private _email: string;

  public get firstName(): string {
    return this._firstName;
  }

  public set firstName(firstName: string) {
    this._firstName = firstName;
  }

  public get lastName(): string {
    return this._lastName;
  }

  public set lastName(lastName: string) {
    this._lastName = lastName;
  }

  public get email(): string {
    return this._email;
  }

  public set email(email: string) {
    this._email = email;
  }
}

HINT: Each of these classes can be very quickly generated using a good IDE, like IntelliJ.

TypeScript Rest Mapper requires you to follow some conventions in declaring the properties of an Entity. The field should be named the same as in the REST API, but with an underscore at the front. A set() and get() method should be provided for every field, but without the underscore.

Note that we don't include the id field explicitly in the entities. This is not needed as the Entity superclass already provides this field as the package assumes that every REST API resource should contain an id.

Decorators

Now, at this point we know what values the API returns and we can start annotating the fields of our entities with the correct decorators. There are 3 important decorators available:

  • Read(): This decorator is used to annotate a field that must be read from your REST API responses. Every field of an Entity annotated with Read() will automatically be filled in when calling retrieve() or list() on an associated Service.
  • Store(): This decorator indicates that the associated field is used to store an entity through the REST API. It will be used when calling store() on an associated Service.
  • Update(): This decorator indicates that the associated field is used to update an entity through the REST API. It will be used when calling update() on an associated Service.

For each of these decorators, there is an array counterpart that should be used when the annotated field contains an array. A limitation currently posed by TypeScript, requires you to explicitly specify the type of objects stored in the array. The following three decorators should be used when working with arrays:

  • ReadArray(type): Treats the associated field of the received response from the API as an array and fill it in completely. Example: ReadArray(Post) when annotating a field _posts: Post[].
  • StoreArray(type): Array analogue of the Store() decorator.
  • UpdateArray(type): Array analogue of the Update() decorator.

Now that we know which decorators exist, we can annotate each of our example entities with the correct decorators.

Post

Title, body and categories all need to be read from an API response. This is why we annotate them with the read() (and readArray() for categories) decorator. The entities stored by the backend REST server also need to be updated when a user creates or modifies a post. All fields should be updated accordingly and that's why we also need to annotate these fields with update() and store(). I'm only repeating the changes made to the fields of the class, the setters and getters remain the same:

export default class Post extends Entity {
  @Read() @Update() @Store()
  private _title: string;
  @Read() @Update() @Store()
  private _body: string;
  @ReadArray(Category) @UpdateArray(Category) @StoreArray(Category)
  private _categories: Category[];
}

Category

The Category class can be annotated analogous to the Post class. A category's name should be read, stored and updated and thus we add all three decorators:

export default class Category extends Entity {
  @Read() @Update() @Store()
  private _name: string;
}

User

Just like the two entities above, we can annotate the fields in the User class with all 3 decorators.

export default class User extends Entity {
  @Read() @Update() @Store()
  private _firstName: string;
  @Read() @Update() @Store()
  private _lastName: string;
  @Read() @Update() @Store()
  private _email: string;
}

Services

We're almost done. We can now simply communicate with the REST server by creating an instance of the Service class. This class needs one parameter: the Entity for which a Service needs to be constructed. To be able to retrieve the post with id 1, we can do this:

let postService: Service<Post> = new Service<Post>(Post, "http://localhost:8080");
let post: Post = postService.retrieve(1);
// post is now a true TypeScript-object with all API information stored inside of it.

Storing a new post is just as easy:

let postService: Service<Post> = new Service<Post>(Post, "http://localhost:8080");
let post: Post = new Post();
post.title = "Lorem ipsum";
post.body = "Dolor sit amet.";

let categoryService: Service<Category> = new Service<Category>(Category, "http://localhost:8080");
let generalCategory = categoryService.retrieve(2);
post.categories = [generalCategory];

postService.store(post);

HINT: You can extend the Service-class and automatically fill in the base URL, if this stays the same over different invocations of the API:

export default class CustomService<T extends Entity> extends Service<T> {
  constructor(x: (new () => T)) {
    // Automatically fill in the base URL here!
    super(x, "http://localhost:8080");
  }
}

Conclusion

At this point, you are able to start consuming simple API's. For more advanced use cases and examples, visit the other pages in this wiki.

Clone this wiki locally