NestboltNestbolt

@nestbolt/likeable

Configuration

LikeableModule.forRoot()/forRootAsync() options and the full LikeableService API.

forRoot

LikeableModule.forRoot() registers LikeableService and the LikeEntity repository globally:

import { LikeableModule } from "@nestbolt/likeable";

@Module({
  imports: [LikeableModule.forRoot()],
})
export class AppModule {}

The options object is currently reserved for future use -- calling forRoot() with no arguments is the standard form. Per-entity behavior is configured via the @Likeable() decorator, not via global options.

forRootAsync

Use forRootAsync() when you need module setup to depend on other providers (e.g., ConfigService):

import { ConfigModule, ConfigService } from "@nestjs/config";
import { LikeableModule } from "@nestbolt/likeable";

@Module({
  imports: [
    ConfigModule.forRoot(),
    LikeableModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (_config: ConfigService) => ({}),
    }),
  ],
})
export class AppModule {}

forRootAsync Options

OptionTypeDescription
importsany[]Modules to import for dependency injection
injectany[]Providers to inject into the factory function
useFactory(...args) => LikeableModuleOptionsFactory function that returns the options

LikeableService API

LikeableService is available via dependency injection after module registration:

@Injectable()
export class MyService {
  constructor(private readonly likeable: LikeableService) {}
}

All methods take the entity constructor as the first argument so the service can resolve the polymorphic type stored in the likes table.

like(entity, entityId, userId)

Records that userId likes (entity, entityId). Idempotent -- a duplicate like for the same (user, entity) pair is silently swallowed:

await likeable.like(Post, post.id, userId);
await likeable.like(Post, post.id, userId); // no-op, no exception

Emits like.liked on the first successful insert. Duplicates do not emit.

unlike(entity, entityId, userId)

Removes the like for (userId, entity, entityId):

await likeable.unlike(Post, post.id, userId);

Emits like.unliked only when a row was actually deleted -- calling unlike() on something that wasn't liked is a no-op and does not emit.

toggle(entity, entityId, userId)

Flips the like state and returns the new state (true if now liked, false if now unliked):

const liked = await likeable.toggle(Post, post.id, userId);
console.log(liked); // true on first call, false on second, etc.

isLikedBy(entity, entityId, userId)

Returns true if userId has liked (entity, entityId):

const liked = await likeable.isLikedBy(Post, post.id, userId);

getLikesCount(entity, entityId)

Returns the total number of likes for an entity:

const count = await likeable.getLikesCount(Post, post.id);

getLikers(entity, entityId)

Returns the list of user IDs that have liked the entity, ordered by most recent first:

const userIds = await likeable.getLikers(Post, post.id);

getUserLikes(entity, userId)

Returns the list of entity IDs of the given type that the user has liked, ordered by most recent first:

const postIds = await likeable.getUserLikes(Post, userId);

getUserLikesCount(entity, userId)

Returns the number of entities of the given type that the user has liked:

const count = await likeable.getUserLikesCount(Post, userId);

isLikeable(entity)

Returns true if the entity class is decorated with @Likeable():

likeable.isLikeable(Post); // true
likeable.isLikeable(SomeOtherEntity); // false

getOptions()

Returns the resolved LikeableModuleOptions passed to forRoot() / forRootAsync():

const options = likeable.getOptions();

Static: LikeableService.getInstance()

Returns the active LikeableService instance, or null if the module hasn't been initialized. This is what the mixin uses internally to bridge entity instances to the service:

const service = LikeableService.getInstance();

You normally don't need to call this directly -- prefer dependency injection or the mixin methods.