NestboltNestbolt

@nestbolt/likeable

Decorator

@Likeable() decorator options for per-entity polymorphic type configuration.

The @Likeable() decorator marks a TypeORM entity as likeable. It stores a small piece of metadata that LikeableService reads to decide what value to write to the likeable_type column of the likes table.

Basic Usage

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
import { Likeable } from "@nestbolt/likeable";

@Likeable()
@Entity("posts")
export class Post {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  title!: string;
}

With no options, the entity's class name ("Post") is used as the polymorphic type.

Options Reference

OptionTypeDefaultDescription
typestringClass name (ctor.name)Polymorphic type stored in the likes.likeable_type column

Custom Polymorphic Type

Set type when you need the value stored in likes.likeable_type to differ from the class name. This is important if:

  • You rename the entity class but want existing rows to keep working
  • You bundle/minify your code and class names get mangled
  • Two entities in different modules share the same class name
@Likeable({ type: "BlogPost" })
@Entity("posts")
export class Post {
  @PrimaryGeneratedColumn("uuid")
  id!: string;
}

All like operations on Post will read and write rows where likeable_type = "BlogPost".

Multiple Entity Types

Each likeable entity gets its own polymorphic type, all stored in the same likes table:

@Likeable() // type = "Post"
@Entity("posts")
export class Post { /* ... */ }

@Likeable() // type = "Comment"
@Entity("comments")
export class Comment { /* ... */ }

@Likeable({ type: "Product" })
@Entity("products")
export class ProductEntity { /* ... */ }

Likes are scoped per type -- liking a Post with id abc and a Comment with id abc creates two distinct rows.

How the Type is Resolved

LikeableService resolves the polymorphic type once per call:

  1. If @Likeable({ type: "..." }) was provided, that string is used.
  2. Otherwise, the entity class's name property is used (Post.name === "Post").

If the entity is missing the @Likeable() decorator entirely, the service still works -- it falls back to the class name. But explicitly decorating is recommended so the intent is clear and so LikeableService.isLikeable() returns true.

Composing with Other Decorators

@Likeable() is a pure metadata decorator with no runtime side effects, so it composes cleanly with any TypeORM or other Nestbolt decorators:

import { HasMedia } from "@nestbolt/medialibrary";
import { Sluggable } from "@nestbolt/sluggable";
import { Likeable } from "@nestbolt/likeable";

@Sluggable({ from: "name" })
@Likeable({ type: "Product" })
@HasMedia({ modelType: "Product" })
@Entity("products")
export class ProductEntity {
  /* ... */
}

Order between Nestbolt decorators does not matter.