Guide
Models & fields
One class produces the schema, the indexes, the database validators, the permissions and the OpenAPI definition.
@ParseClass
Registers the Parse subclass and everything derived from it. Applied to a class extending BaseModel.
import {
ParseClass, BaseModel, ParseField, BeforeSave, validateOrThrow,
} from 'parse-server-kit';
@ParseClass('Product', {
description: 'A product in the catalogue',
clp: { find: { requiresAuthentication: true } },
compoundIndexes: [{ fields: ['status', 'createdAt'] }],
})
export default class Product extends BaseModel {
constructor() { super('Product'); }
}
| Option | What it does |
|---|---|
clp | Class-level permissions, applied to the Parse schema |
protectedFields | Fields hidden from given roles |
ACL | A default ACL template for new objects |
description | Shown in the generated OpenAPI document |
compoundIndexes | Multi-field indexes, created at boot |
A Parse.Role subclass is detected and deliberately not passed to registerSubclass, which Parse rejects.
@ParseField
Defines a getter and setter on the prototype that read and write Parse's attribute store. That means product.name is the supported API, not product.get('name').
@ParseField({ type: 'String', required: true, maxLength: 200 })
declare name: string;
@ParseField({ type: 'String', unique: true })
declare sku: string;
// A Pointer must name its target, or fromParams() skips it.
@ParseField({ type: 'Pointer', targetClass: 'Category' })
declare category: any;
declare, not !
The declaration exists only to give TypeScript the type — the storage belongs to Parse.
Written as name!: string, TypeScript emits a real class field that shadows
the accessor whenever useDefineForClassFields is on (its default from
target: ES2022). @ParseClass repairs that automatically, so
neither form breaks, but declare states what is actually true.
Options
| Option | Applies to | Effect |
|---|---|---|
type | all | String, Number, Boolean, Date, Object, Array, GeoPoint, File, Bytes, Polygon, Pointer, Relation |
required | all | Enforced by validateOrThrow and by the database validator |
targetClass | Pointer, Relation | Mandatory — omitting it throws at import |
unique | all | Unique index, created at boot |
index | all | true, 1 or -1 |
min / max | Number | Range, enforced in code and in MongoDB |
minLength / maxLength | String | Length bounds |
enum | String | Allowed values |
pattern | String | Regular expression |
geo | GeoPoint | 2dsphere index |
ttlSeconds | Date | TTL index — rows expire N seconds after this date |
Options are validated when the file is imported, not when a request arrives. A Pointer without targetClass, min on a String, an invalid regex, geo combined with index — all throw at boot, where you will see them.
BaseModel
// A reference by id, with no fetch.
const ref = Category.pointer('abc123');
// Build a typed instance from request params, reading @ParseField
// metadata to convert pointers, arrays of pointers, dates and geopoints.
const product = Product.fromParams(req.params);
Use fromParams in create and update rather than a series of set() calls. It converts a Pointer given null or {} to an explicit clear, and converts an Array to pointers only when the field declares a targetClass.
Parse.Query returns Parse.Object, not your class, so typed
property access is unavailable until you cast: const p = row as Product.
This is a limitation of Parse's own typings, not something this library changes.
Validation
@BeforeSave()
static async onBeforeSave(req: Parse.Cloud.BeforeSaveRequest<Product>) {
validateOrThrow(req.object); // checks every rule declared above
}
validateOrThrow checks required, min/max, minLength/maxLength, enum and pattern, and throws a Parse VALIDATION_ERROR listing every failure at once. Putting it in @BeforeSave means it runs on every save path — cloud function, REST, dashboard or script.
applyMongoValidators() pushes the same constraints into MongoDB $jsonSchema validators, so the database enforces them even for writes that never touch your code.