Skip to content

Example App

apps/example-api is the reference application in this repository.

It demonstrates how Semitra’s subsystems fit together in one Worker app.

apps/example-api/
app/
channels/
controllers/
jobs/
mailboxes/
mailers/
models/
policies/
resources/
config/
application.ts
routes.ts
db/
migrate/
schema.ts
src/
index.ts

If you are reading the example app for orientation, start in this order:

  1. config/routes.ts
  2. app/controllers/application_controller.ts
  3. app/controllers/api/v1/posts_controller.ts
  4. app/models/application_record.ts
  5. app/models/post.ts
  6. app/resources/post_resource.ts
  7. app/policies/post_policy.ts

That path mirrors Semitra’s request flow.

  • current-user hydration through a shared application controller
  • tenant resolution through headers and subdomains
  • record schemas defined with s.object(...)
  • policy guards enforced at the controller level
  • resource-based serialization
  • job execution that delegates to a mailer
  • channel broadcasting with validated messages

Representative snippets from the repo:

config/application.ts
resolver: composeTenantResolvers(
headerTenantResolver(),
subdomainTenantResolver()
)
// app/controllers/api/v1/posts_controller.ts
await this.authorize(post, "show");
return this.renderResource(post);
// app/resources/post_resource.ts
this.attribute("excerpt", (post) =>
typeof post.content === "string" ? post.content.slice(0, 32) : null
);

For POST /api/v1/posts:

  1. the route resolves to api/v1/posts#create
  2. the controller validates the post payload
  3. the controller authorizes against PostPolicy
  4. this.model(Post) binds the record class to the request runtime
  5. the record validates and persists through D1
  6. the controller renders PostResource
  7. the client receives serialized JSON

The same pattern shows up in the non-HTTP subsystems too:

  • WelcomeEmailJob validates its payload and delegates delivery to a mailer
  • NotificationsChannel validates incoming messages and broadcasts to a room

Semitra is still best understood by reading live framework code and a live app together. This example is the reference implementation for the current repo, so it is the right place to verify how the docs map to reality.