Skip to content

Resources

SemitraResource defines what a client sees.

Resources are the API boundary for response shape.

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.

  • expose attributes explicitly
  • compute derived fields
  • serialize relationships
  • optionally validate the final response payload

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

Resources can access request-aware context including:

  • request
  • params
  • validatedParams
  • currentUser
  • currentTenant
  • controller
  • the current action
  • policy lookup helpers

That means resources can make context-aware decisions when needed without forcing serialization logic back into the controller.