Resources
SemitraResource defines what a client sees.
Resources are the API boundary for response shape.
Example resource
Section titled “Example resource”import BaseApplicationResource from "./application_resource.ts";import CommentResource from "./comment_resource.ts";
export default class PostResource extends BaseApplicationResource<Post> { static { this.attributes("id", "title", "content", "createdAt"); this.attribute("excerpt", (post) => typeof post.content === "string" ? post.content.slice(0, 32) : null ); this.hasMany("comments", () => CommentResource); }}In the reference app, PostsController returns this resource instead of
assembling JSON manually.
Good fits for this pattern include:
- a posts index that exposes a computed
excerpt - a detail endpoint that serializes related records
- a feed endpoint that hides fields the client does not need
The reference app includes a CommentResource to show relationship
serialization. Treat that as a serializer example unless your app also defines
the matching comment record, migration, and association.
What resources do
Section titled “What resources do”- expose attributes explicitly
- compute derived fields
- serialize relationships
- optionally validate the final response payload
Why resources matter
Section titled “Why resources matter”Without resources, controller actions tend to accumulate ad hoc JSON shaping.
Semitra keeps that logic in one place so:
- response fields are declarative
- controllers stay focused on orchestration
- field visibility can coordinate with policy logic
- response contracts can be reused and tested
Resource context
Section titled “Resource context”Resources can access request-aware context including:
requestparamsvalidatedParamscurrentUsercurrentTenantcontroller- the current action
- policy lookup helpers
That means resources can make context-aware decisions when needed without forcing serialization logic back into the controller.