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
include
andexclude
are present,include
always overridesexclude
.
Recommended Best Practicesโ
- โ
Prefer
includeFields
for precise control over model output - ๐ซ Avoid defining both
includeFields
andexcludeFields
on the same model - ๐ Use
excludeFields
to 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.