Extensions
Resources
Your internal model and your public representation are not the same thing. A resource is the deliberate projection between them: the place where you decide, once, what the world sees and what stays private.
#Install
npm i @stone-js/resources#Define the projection
The principle
Returning raw models leaks internals and couples your API to your storage. A resource draws a stable line between the two, so the model can change without breaking the contract, and secrets never slip out by accident.
In Stone.js
defineResource(transform) declares the projection. Use .item()for one model and .collection() for many; sparse fieldsets, conditional fields and the { data, meta } envelope are handled for you.
import { defineResource, only } from '@stone-js/resources'
export const taskResource = defineResource((task) => ({
id: task.id,
title: task.title,
done: task.done,
createdAt: task.createdAt.toISOString()
// note: no ownerId, no internal flags. The projection is the contract.
}))#Using it in a handler
@Get('/')
list () {
return taskResource.collection(this.tasks.list()) // many
}
@Get('/:id')
show (event) {
return taskResource.item(this.tasks.find(event.get('id')), only(['id', 'title'])) // sparse
}#Helpers
| Helper | Type | Description |
|---|---|---|
| .item(model, ctx?) | one | Project a single model. |
| .collection(models, ctx?) | many | Project an array of models. |
| .envelope(...) | wrap | Wrap the result in a { data, meta } envelope. |
| only([...]) / except([...]) | fields | Sparse fieldsets: include or drop fields. |
| whenIncluded(ctx, key, fn) | conditional | Include related data only when requested. |