@mask — Output Masking
Masks field values on read using built-in presets or custom directives. The raw value is always stored — masking only affects query results.
Syntax
src/interfaces.ts
// @mask:<preset>
ssn?: string; // @mask:ssn
creditCard?: string; // @mask:creditcard
maskedEmail?: string; // @mask:email
phoneNumber?: string; // @mask:phone
showFirst4?: string; // @mask:showFirst:4
showLast4?: string; // @mask:showLast:4
starMasked?: string; // @mask:char:*
patternMasked?: string;// @mask:pattern:###-##-####Presets
| Preset | Input | Output |
|---|---|---|
ssn | 987-65-4321 | ***-**-4321 |
creditcard | 4111-1111-1111-1111 | ****-****-****-1111 |
email | john.doe@example.com | j*****@example.com |
phone | 555-123-4567 | ***-***-4567 |
Custom Directives
| Directive | Input | Output |
|---|---|---|
showFirst:N | ABCDEFGHIJ | ABCD****** |
showLast:N | ABCDEFGHIJ | ******GHIJ |
char:X | secret-value | ********alue |
pattern:... | 123-45-6789 | ###-##-#### |
Bypassing Masking
src/admin.ts
// .withoutMasking() returns raw values for privileged callers
const unmasked = await User.query()
.withoutMasking()
.where("name", "=", "Alice")
.get();
console.log(unmasked[0].ssn);
// "987-65-4321" (raw value)Errors
| Issue | Cause | Fix |
|---|---|---|
| Unknown preset | Typo in preset name | Use one of: ssn, creditcard, email, phone |
| Non-string value masked | @mask only works on string fields | Ensure the field type is string |
Conventions
- Masking is applied on read — the database always stores the full value
- Use
.withoutMasking()for admin endpoints or internal use - Presets are format-aware (e.g.
ssnkeeps last 4 digits regardless of input format)