NestJS SDK

Integrate Perly churn prevention into your NestJS application with a dedicated module, injectable resolver, and typed client.

@perly/nestjsv1.0.05 minutes

Installation

npm install @perly/nestjs @perly/core

Usage

Register the PerlyModule in your root application module. The module sets up the middleware that intercepts every HTTP request and resolves the current user. Note that PerlyUserResolver is imported from @perly/core.

// app.module.ts
import { Module } from '@nestjs/common';
import { PerlyModule } from '@perly/nestjs';
import { PerlyUserResolver } from '@perly/core';
import { AppPerlyUserResolver } from './perly-user.resolver';

@Module({
  imports: [
    PerlyModule.forRoot({
      apiKey: process.env.PERLY_API_KEY,
    }),
  ],
  controllers: [/* ...controllers */],
  providers: [
    { provide: PerlyUserResolver, useClass: AppPerlyUserResolver },
  ],
})
export class AppModule {}

User Resolver

Create a resolver that maps incoming requests to Perly user profiles. The resolver is called automatically by the middleware on every request. Because it's a NestJS injectable, you can inject other services like UserService to look up user data.

// perly-user.resolver.ts
import { Injectable } from '@nestjs/common';
import { PerlyUser, PerlyUserResolver, PerlyBuilder } from '@perly/core';
import { UserService } from './user.service';

@Injectable()
export class AppPerlyUserResolver implements PerlyUserResolver {
  constructor(private readonly usersService: UserService) {}

  async resolve(req: Request): Promise<PerlyUser | null> {
    const userId = req.headers['user-id'];
    if (!userId) return null;
    const user = await this.usersService.findById(userId);

    return new PerlyBuilder()
      .setId(user.id)
      .setMetadata({
        plan: user.plan,
        region: user.region,
      })
      .linkStripeById(user.stripeId)
      .linkHubspotById(user.hubspotId)
      .build();
  }
}

Tracking Events

Inject the Perly client into any service to track custom events that indicate customer engagement.

import { Inject, Injectable } from '@nestjs/common';
import { PERLY_CLIENT, PerlyClient } from '@perly/nestjs';

@Injectable()
export class OnboardingService {
  constructor(@Inject(PERLY_CLIENT) private perly: PerlyClient) {}

  async complete(userId: string) {
    await this.perly.track(userId, 'onboarding_completed');
  }

  async inviteTeamMember(userId: string, email: string) {
    await this.perly.track(userId, 'team_member_invited', { email });
  }
}

Expansion Signals

Send signals when a customer approaches or hits usage limits. These drive upsell workflows in Perly.

// In any injectable service
await this.perly.signal(userId, 'seat_limit_near', {
  current: 48,
  limit: 50,
});

await this.perly.signal(userId, 'api_usage_high', {
  current: 9500,
  limit: 10000,
  period: 'monthly',
});