Skip to main content

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.
Priority Rule

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.

src/app/models/user.ts
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",
];
}
Security Tip

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.

untitled
const user = await Users.findOneBy({ id: 1 });
res.json(user);
When you open the link http://localhost:3000/users/1 in your browser, you should see the result shown below.
http://localhost:3000/users/1
{ "firstName": "John", "lastName": "Doe" }

Property-Level Declaration with @IncludeField()โ€‹

You can also define included fields directly on properties using decorators.

src/app/models/user.ts
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;
}
untitled
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.

src/app/models/user.ts
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โ€‹

src/app/models/user.ts
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.

src/app/models/user.ts
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.

untitled
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:

untitled
const user = await Users.findOneBy({ id: 1 });

user.updateIncludeField(["balance"], true);

const output = user.toClient();
console.log(output); // { "balance": 200.0 }

Parametersโ€‹

ParameterTypeDescription
fieldsModelIncludeFieldsMethod<Users>[]Fields to include
overridebooleanIf true, replaces all existing definitions

Retrieve Current Include Fieldsโ€‹

Use getIncludeField() to access the current include rules at runtime.

untitled
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โ€‹

src/app/models/user.ts
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.

src/app/models/user.ts
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().

src/app/models/user.ts
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.

untitled
const user = await Users.findOneBy({ id: 1 });

user.updateExcludeField(["password", "ssn"], true);

Retrieve Current Exclude Fieldsโ€‹

untitled
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()
untitled
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(), and setIncludeFields() are combined.
  • Fields from excludeFields, @ExcludeField(), and setExcludeFields() are combined.
  • When both include and exclude are present, include always overrides exclude.
  • โœ… Prefer includeFields for precise control over model output
  • ๐Ÿšซ Avoid defining both includeFields and excludeFields 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.