Guide

Triggers

All 21 Parse trigger types as decorators, declared next to the thing they act on.

Declaring a trigger

import {
  ParseClass, BaseModel, BeforeSave, Cron, CronSchedule,
} from 'parse-server-kit';

@ParseClass('Product')
export default class Product extends BaseModel {
  @BeforeSave()
  static async onBeforeSave(req: Parse.Cloud.BeforeSaveRequest<Product>) {
    if (!req.object.get('name')) {
      throw new Parse.Error(142, 'Name is required');
    }
  }
}

Declaring the trigger on the model means you never repeat the class name, and the logic sits beside the fields it validates.

A trigger needs @ParseClass Trigger decorators cannot register anything on their own — they do not know the Parse class name until @ParseClass supplies it, so they park in metadata and wait. On a class that never gets @ParseClass they wait forever. Since 3.0, TriggerRegistry.initialize() names any class still waiting instead of leaving you to notice the trigger never fired.

Every trigger

DecoratorRegisters as
@BeforeSave @AfterSavebeforeSave / afterSave
@BeforeDelete @AfterDeletebeforeDelete / afterDelete
@BeforeFind @AfterFindbeforeFind / afterFind
@BeforeLogin @AfterLogin @AfterLogoutauth triggers, no class name
@BeforePasswordResetRequestparse-server 8.5+
@BeforeSaveFile @AfterSaveFilebeforeSave(Parse.File, …)
@BeforeDeleteFile @AfterDeleteFilebeforeDelete(Parse.File, …)
@BeforeFindFile @AfterFindFileparse-server 8.1+
@BeforeSaveConfig @AfterSaveConfigParse Config, 7.3+
@BeforeConnect @BeforeSubscribe @AfterEventLiveQuery

File and Config triggers pass the class itself rather than a name. parse-server removed the older beforeSaveFile() style methods; the library handles that translation, so the decorators work unchanged across versions.

One trigger per class and type — a second registration warns and replaces the first.

Scheduled jobs

class Jobs {
  @Cron({ schedule: CronSchedule.DAILY_MIDNIGHT, description: 'Nightly cleanup' })
  static async cleanup() { … }
}

CronSchedule covers the usual patterns from EVERY_MINUTE to YEARLY; any valid cron expression also works and is validated at initialize(). CronRegistry exposes stopJob, startJob, stopAll and runNow for control at runtime.

Without node-cron installed, registration warns and skips rather than failing to start.