Include and Exclude Fields
Alapa offers robust mechanisms for controlling which fields are exposed to clients through includeFields and excludeFields.
These configurations help sanitize model output for browsers and APIs, protecting sensitive data while improving clarity and consistency.
Both decorators and dynamic runtime utilities are supported, giving you maximum flexibility while keeping your models secure.
Core Conceptโ
includeFieldsโ explicitly define which fields should be included in client responses.excludeFieldsโ define which fields must be excluded from client responses.
When both includeFields and excludeFields are defined, includeFields takes precedence and excludeFields is ignored.
Static Declaration with includeFieldsโ
Define output-safe fields statically by assigning them to the protected includeFields property.
import { Model, Column, ModelIncludeFields, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
passwordResetToken: string;
@Column()
password: string;
protected includeFields: ModelIncludeFields<Users>[] = [
"firstName",
"lastName",
];
}
Avoid using "*" to include all fields, as it may unintentionally expose sensitive attributes to clients.
Implementing Outputโ
When a model is serialized using methods like res.json(), toClient(), or toAPI(), only the fields in includeFields will appear in the response.
const user = await Users.findOneBy({ id: 1 });
res.json(user);
{ "firstName": "John", "lastName": "Doe" }
Property-Level Declaration with @IncludeField()โ
You can also define included fields directly on properties using decorators.
import { Model, Column, IncludeField, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
password: string;
@IncludeField()
@Column()
firstName: string;
@IncludeField()
@Column()
lastName: string;
@Column()
balance: number;
@Column()
isActive: boolean;
}
const user = await Users.findOneBy({ id: 1 });
const output = user.toClient();
console.log(output); // { "firstName": "John", "lastName": "Doe" }
setIncludeFields() Methodโ
For dynamic include logic, override the setIncludeFields() method to return specific fields programmatically.
import { Model, Column, ModelIncludeFields, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
password: string;
@Column()
firstName: string;
@Column()
lastName: string;
protected setIncludeFields(): ModelIncludeFields<Users>[] {
return ["firstName", "lastName"];
}
}
Conditional Include Fieldsโ
You can include fields conditionally using booleans or functions.
Object-Based Conditional Fieldsโ
import { Model, Column, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
role: string;
@Column()
balance: number;
protected includeFields = [
"firstName",
"lastName",
{
balance: (user) => user.role === "admin",
},
];
}
Decorator-Based Conditional Fieldsโ
You can apply conditions directly using the @IncludeField() decorator with a function.
import { Model, Column, IncludeField, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@IncludeField()
@Column()
firstName: string;
@IncludeField()
@Column()
lastName: string;
@IncludeField((user) => user.role === "admin")
@Column()
balance: number;
}
updateIncludeField()โ
You can update the list of included fields at runtime.
const user = await Users.findOneBy({ id: 1 });
user.updateIncludeField(["balance"]);
const output = user.toClient();
console.log(output); // { "firstName": "John", "lastName": "Doe", "balance": 200.0 }
Override Modeโ
To fully replace existing include definitions:
const user = await Users.findOneBy({ id: 1 });
user.updateIncludeField(["balance"], true);
const output = user.toClient();
console.log(output); // { "balance": 200.0 }
Parametersโ
| Parameter | Type | Description |
|---|---|---|
fields | ModelIncludeFieldsMethod<Users>[] | Fields to include |
override | boolean | If true, replaces all existing definitions |
Retrieve Current Include Fieldsโ
Use getIncludeField() to access the current include rules at runtime.
const user = await Users.findOneBy({ id: 1 });
const includeFields = user.getIncludeField();
console.log(includeFields); // ["firstName", "lastName"]
Exclude Fieldsโ
Use excludeFields to define fields that should never be exposed in client responses.
Static Declaration with excludeFieldsโ
import { Model, Column, ModelExcludeFields, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
password: string;
@Column()
ssn: string;
protected excludeFields: ModelExcludeFields<Users>[] = ["password", "ssn"];
}
Decorator-Based Excludesโ
You can use the @ExcludeField() decorator with or without conditions.
import { Column, ExcludeField } from "alapa";
@ExcludeField()
@Column()
password: string;
@ExcludeField((user) => user.role !== "admin")
@Column()
ssn: string;
setExcludeFields() Methodโ
You can dynamically define excluded fields by overriding setExcludeFields().
import { Model, Column, ModelExcludeFields, TableModel } from "alapa";
@TableModel()
export class Users extends Model {
@Column()
password: string;
@Column()
ssn: number;
@Column()
firstName: string;
@Column()
lastName: string;
protected setExcludeFields(): ModelExcludeFields<Users>[] {
return ["ssn", "password"];
}
}
Update excludeFields at Runtimeโ
Use updateExcludeField() to modify excluded fields dynamically.
const user = await Users.findOneBy({ id: 1 });
user.updateExcludeField(["password", "ssn"], true);
Retrieve Current Exclude Fieldsโ
const user = await Users.findOneBy({ id: 1 });
const excluded = user.getExcludeField();
console.log(excluded); // ["password", "ssn"]
Methods That Respect Include/Exclude Fieldsโ
The following model methods automatically apply both includeFields and excludeFields filtering:
Model.toClient()Model.toAPI()Model.toJSON()Model.sanitize()Model.sterilize()res.api()
const user = await Users.findOneBy({ id: 1 });
const output = user.toClient();
console.log(output); // { "firstName": "John", "lastName": "Doe" }
Unified Field Resolution & Best Practicesโ
Alapa intelligently merges all include and exclude definitions using the following rules:
- Fields from
includeFields,@IncludeField(), andsetIncludeFields()are combined. - Fields from
excludeFields,@ExcludeField(), andsetExcludeFields()are combined. - When both
includeandexcludeare present,includealways overridesexclude.
Recommended Best Practicesโ
- โ
Prefer
includeFieldsfor precise control over model output - ๐ซ Avoid defining both
includeFieldsandexcludeFieldson the same model - ๐ Use
excludeFieldsto hide specific sensitive attributes - ๐งช Enable strict development checks if available
- ๐ค Regularly audit exposed fields to prevent accidental data leaks
By carefully managing include and exclude configurations, you can keep your APIs predictable, maintainable, and secure.