Serialization
Use toJson() for one model and toArray() on a collection. Both omit ORM internals and hidden columns.
| Method | On | Returns |
|---|---|---|
model.toJson() | Model | One plain object |
model.toArray() | Model | Same payload as toJson() |
collection.toArray() | ModelCollection | Array of plain objects |
toJson() returns a plain object, not a JSON string. It is an explicit helper, and its spelling differs from JavaScript's automatic toJSON() hook. JSON.stringify(model) does not call toJson(). Use JSON.stringify(model.toJson()) for a JSON string with hidden fields omitted, or res.json(model.toJson()) in an HTTP handler.
Model toJson() / toArray()
class User extends Model {
override fillable = ['name', 'email', 'password']
override hidden = ['password']
}
const user = await User.findOrFail(userId)
return user.toJson()
// {
// id: 1,
// name: 'Jane Doe',
// email: 'jane@example.com'
// }
// password omitted (hidden)
// fillable, hidden, modelName, table omitted (internals)What is excluded
| Category | Keys |
|---|---|
| ORM internals | fillable, hidden, modelName, table |
| Hidden attributes | whatever you list in hidden |
| Functions | methods on the instance |
Collection toArray()
Model.all() and paginate().data return a ModelCollection with the same helper:
const users = await User.all()
return users.toArray()
// [{ id: 1, name: '...' }, { id: 2, name: '...' }]const page = await User.orderBy('id').paginate(20, 1)
return {
data: page.data.toArray(),
meta: {
total: page.total,
current_page: page.current_page,
last_page: page.last_page
}
}Hidden fields after reads
create() strips hidden attributes from the returned instance. find(), first(), all(), and paginate() retain any hidden attributes selected by the query. Serialisation with toArray() or toJson() omits hidden attributes without changing the model. Always serialise models before returning them from an API or logging their data.
const user = await User.findOrFail(userId)
// The model can still hold user.password.
console.log(user.toArray()) // password is omitted from the plain objectNesting related data
Relations return models (or arrays). Map them yourself:
const farmer = await Farmer.findOrFail(id)
const farms = await farmer.farms().get()
return {
...farmer.toArray(),
farms: farms.map((f) => f.toArray())
}API response helpers
// utils/respond.ts
import type { Model, ModelCollection } from 'mevn-orm'
export function jsonModel(model: Model) {
return model.toArray()
}
export function jsonCollection(models: ModelCollection<Model> | Model[]) {
if ('toArray' in models && typeof models.toArray === 'function') {
return models.toArray()
}
return models.map((m) => m.toArray())
}
export function jsonPage<T extends Model>(result: {
data: ModelCollection<T>
total: number
per_page: number
current_page: number
next_page: number | null
prev_page: number | null
last_page: number
}) {
return {
data: result.data.toArray(),
meta: {
total: result.total,
per_page: result.per_page,
current_page: result.current_page,
next_page: result.next_page,
prev_page: result.prev_page,
last_page: result.last_page
}
}
}Usage:
const page = await Post.where({ published: true }).paginate(10, 2)
return jsonPage(page)Security checklist
- Put secrets (
password,token,ssn) inhidden. - Never spread a model into
res.json(user)if you have not verified hidden fields — usetoArray(). - Hash passwords before
create/save; serialization only hides, it does not hash.
See also Security.